CVE-2022-23475 (published 2022-12-06T20:15Z)

daloRADIUS is an open source RADIUS web management application. daloRadius 1.3 and prior are vulnerable to a combination cross site scripting (XSS) and cross site request forgery (CSRF) vulnerability which leads to account takeover in the mng-del.php file because of an unescaped variable reflected in the DOM on line 116. This issue has been addressed in commit `ec3b4a419e`. Users are advised to manually apply the commit in order to mitigate this issue. Users may also mitigate this issue with in two parts 1) The CSRF vulnerability can be mitigated  by making the daloRadius session cookie to samesite=Lax or by the implimentation of a CSRF token in all forms. 2) The XSS vulnerability may be mitigated by escaping it or by introducing a Content-Security policy.\n\n\n

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-352
cwe_name: Cross-Site Request Forgery (CSRF)
description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
extended_description: When a web server is designed to receive a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for an attacker to trick a client into making an unintentional request to the web server which will be treated as an authentic request. This can be done via a URL, image load, XMLHttpRequest, etc. and can result in exposure of data or unintended code execution.
         
cwe_id: CWE-79
cwe_name: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
extended_description: 
            Cross-site scripting (XSS) vulnerabilities occur when:
            
               Untrusted data enters a web application, typically from a web request.
               The web application dynamically generates a web page that contains this untrusted data.
               During page generation, the application does not prevent the data from containing content that is executable by a web browser, such as JavaScript, HTML tags, HTML attributes, mouse events, Flash, ActiveX, etc.
               A victim visits the generated web page through a web browser, which contains malicious script that was injected using the untrusted data.
               Since the script comes from a web page that was sent by the web server, the victim's web browser executes the malicious script in the context of the web server's domain.
               This effectively violates the intention of the web browser's same-origin policy, which states that scripts in one domain should not be able to access resources or run code in a different domain.
            
            There are three main kinds of XSS:
            
               
                  Type 1: Reflected XSS (or Non-Persistent) - 
         			The server reads data directly from the HTTP request and reflects it back in the HTTP response. Reflected XSS exploits occur when an attacker causes a victim to supply dangerous content to a vulnerable web application, which is then reflected back to the victim and executed by the web browser. The most common mechanism for delivering malicious content is to include it as a parameter in a URL that is posted publicly or e-mailed directly to the victim. URLs constructed in this manner constitute the core of many phishing schemes, whereby an attacker convinces a victim to visit a URL that refers to a vulnerable site. After the site reflects the attacker's content back to the victim, the content is executed by the victim's browser.
               
                  Type 2: Stored XSS (or Persistent) - 
               The application stores dangerous data in a database, message forum, visitor log, or other trusted data store. At a later time, the dangerous data is subsequently read back into the application and included in dynamic content. From an attacker's perspective, the optimal place to inject malicious content is in an area that is displayed to either many users or particularly interesting users. Interesting users typically have elevated privileges in the application or interact with sensitive data that is valuable to the attacker. If one of these users executes malicious content, the attacker may be able to perform privileged operations on behalf of the user or gain access to sensitive data belonging to the user. For example, the attacker might inject XSS into a log message, which might not be handled properly when an administrator views the logs.
            
               
                  Type 0: DOM-Based XSS - 
               In DOM-based XSS, the client performs the injection of XSS into the page; in the other types, the server performs the injection. DOM-based XSS generally involves server-controlled, trusted script that is sent to the client, such as Javascript that performs sanity checks on a form before the user submits it. If the server-supplied script processes user-supplied data and then injects it back into the web page (such as with dynamic HTML), then DOM-based XSS is possible.
            
            
            Once the malicious script is injected, the attacker can perform a variety of malicious activities. The attacker could transfer private information, such as cookies that may include session information, from the victim's machine to the attacker. The attacker could send malicious requests to a web site on behalf of the victim, which could be especially dangerous to the site if the victim has administrator privileges to manage that site. Phishing attacks could be used to emulate trusted web sites and trick the victim into entering a password, allowing the attacker to compromise the victim's account on that web site. Finally, the script could exploit a vulnerability in the web browser itself possibly taking over the victim's machine, sometimes referred to as "drive-by hacking."
            In many cases, the attack can be launched without the victim even being aware of it. Even with careful users, attackers frequently use a variety of methods to encode the malicious portion of the attack, such as URL encoding or Unicode, so the request looks less suspicious.
         
         

Fix information:
repo_url: https://github.com/lirantal/daloradius
commit_details: repo_url: https://github.com/lirantal/daloradius
author: Filippo
author_date: 2022-11-28 16:12:03+01:00
msg: Improvements to PDF ticket printing and security issues fix/mitigation (#297)

* Improved notification and ticket printing features

* minor template fixes

* fixed redirection on checklogin

* addressed some security issues
repository: repo_url: https://github.com/lirantal/daloradius
repo_name: lirantal/daloradius
description: daloRADIUS is an advanced RADIUS web management application for managing hotspots and general-purpose ISP deployments. It features user management, graphical reporting, accounting, a billing engine, and integrates with OpenStreetMap for geolocation. The system is based on FreeRADIUS with which it shares access to the backend database.
repo_language: PHP
owner: lirantal
file_changes: filename: bill-payments-list.php
change_type: ModificationType.MODIFY
diff: @@ -252,26 +252,14 @@
     }
     
     include('library/closedb.php');
-?>
-                
-        </div><!-- #contentnorightbar -->
-        
-        <div id="footer">
-<?php
     include('include/config/logging.php');
-    include('page-footer.php');
+    
+    $inline_extra_js = "
+var tooltipObj = new DHTMLgoodies_formTooltip();
+tooltipObj.setTooltipPosition('right');
+tooltipObj.setPageBgColor('#EEEEEE');
+tooltipObj.setTooltipCornerSize(15);
+tooltipObj.initFormFieldTooltip()";
+    
+    print_footer_and_html_epilogue($inline_extra_js);
 ?>
-        </div><!-- #footer -->
-    </div>
-</div>
-
-<script>
-    var tooltipObj = new DHTMLgoodies_formTooltip();
-    tooltipObj.setTooltipPosition('right');
-    tooltipObj.setPageBgColor('#EEEEEE');
-    tooltipObj.setTooltipCornerSize(15);
-    tooltipObj.initFormFieldTooltip();
-</script>
-
-</body>
-</html>

programming_language: PHP
filename: bill-pos-list.php
change_type: ModificationType.MODIFY
diff: @@ -245,6 +245,8 @@
 ?>
     </table>
 
+    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">
+
 </form>
 
 <?php

programming_language: PHP
filename: bill-pos-new.php
change_type: ModificationType.MODIFY
diff: @@ -43,13 +43,14 @@
         strtolower($configValues['CONFIG_DB_PASSWORD_ENCRYPTION']) !== 'cleartext') {
         $valid_passwordTypes = array_diff($valid_passwordTypes, array("Cleartext-Password"));
     }
+    $passwordType = (array_key_exists('passwordType', $_POST) && isset($_POST['passwordType']) &&
+                     in_array($_POST['passwordType'], $valid_passwordTypes)) ? $_POST['passwordType'] : "";
+
 
     isset($_POST['username']) ? $username = $_POST['username'] : $username = "";
     isset($_POST['password']) ? $password = $_POST['password'] : $password = "";
     isset($_POST['planName']) ? $planName = $_POST['planName'] : $planName = "";
     isset($_POST['profiles']) ? $profiles = $_POST['profiles'] : $profiles = "";
-    $passwordType = (array_key_exists('passwordType', $_POST) && isset($_POST['passwordType']) &&
-                         in_array($_POST['passwordType'], $valid_passwordTypes)) ? $_POST['passwordType'] : "";
     isset($_POST['notificationWelcome']) ? $notificationWelcome = $_POST['notificationWelcome'] : $notificationWelcome = "";
     
 

programming_language: PHP
filename: notificationsBatchDetails.php
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
@@ -15,353 +15,263 @@
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
 
-  include ("library/checklogin.php");
-  $operator = $_SESSION['operator_user'];
-
-	require_once(dirname(__FILE__)."/../../notifications/processNotificationBatchDetails.php");
-	require_once(dirname(__FILE__)."/../../library/config_read.php");
-
-	isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
-	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";
-
-	if ($batch_name != "") {
-		$customerInfo = @getBatchDetails($batch_name);
-
-		$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
-		$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
-		$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
-		$from = $configValues['CONFIG_MAIL_SMTPFROM'];
-
-		$pdfDocument = @createBatchDetailsNotification($customerInfo);
-
-		if ($destination == "download") {
-
-			header("Content-type: application/pdf");
-			header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
-			print $pdfDocument;
-
-		} else if ($destination == "email") {
-
-			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
-			header("Location: ".$_SERVER['HTTP_REFERER']);
-		}
-
-	}
-
-
-	function getBatchDetails($batch_name = NULL) {
-
-		require(dirname(__FILE__)."/../../library/opendb.php");
-		require_once(dirname(__FILE__)."/../../lang/main.php");
-
-		global $configValues;
-
-		if ($batch_name == NULL || empty($batch_name))
-			exit;
-
-
-
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-
-		$customerInfo = array();
-
-		$sql = "SELECT ".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".
-
-			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
-			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
-			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
-			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
-			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".
-
-
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
-			" FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".
-
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".
-
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
-			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".
-
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".
-
-			" WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '".$dbSocket->escapeSimple($batch_name)."' ".
-			" GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";
-
-		$res = $dbSocket->query($sql);
-
-		$batch_details = "";
-
-		$batch_details .= "<table $tableTags><tr $tableTrTags>
-					<td>
-			".t('all','BatchName')."
-			</td>
-
-			<td>
-			".t('all','HotSpot')."
-			</td>
-
-			<td>
-			".t('all','BatchStatus')."
-			</td>
-
-			<td>
-			".t('all','TotalUsers')."
-			</td>
-
-			<td>
-			".t('all','ActiveUsers')."
-			</td>
-
-			<td>
-			".t('all','PlanName')."
-			</td>
-
-			<td>
-			".t('all','PlanCost')."
-			</td>
-
-			<td>
-			".t('all','BatchCost')."
-			</td>
-
-			<td>
-			".t('all','CreationDate')."
-			</td>
-
-			<td>
-			".t('all','CreationBy')."
-			</td>
-
-			</tr>";
-
-		$active_users_per = 0;
-		$total_users = 0;
-		$active_users = 0;
-		$batch_cost = 0;
-
-		$hotspot_name = "";
-		$batch_id = "";
-		$planname = "";
-
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-
-			$batch_id = $row['id'];
-			$hotspot_name = $row['HotspotName'];
-			$batch_status = $row['batch_status'];
-			$plancost = $row['plancost'];
-			$total_users = $row['total_users'];
-			$active_users = $row['active_users'];
-			$batch_cost = ($active_users * $plancost);
-			$plan_currency = $row['plancurrency'];
-			$planname = $row['planname'];
-
-
-			$batch_details .= "
-					<tr>
-
-					<td>".$row['batch_name']."
-
-					</td>
-
-					<td>".$hotspot_name."
-
-					</td>
-
-					<td>".$batch_status."
-
-					</td>
-
-					<td>".$total_users."
-
-					</td>
-
-					<td>".$active_users."
-
-					</td>
-
-					<td>".
-						$row['planname']."
-					</td>
-
-					<td>".$plancost."
-					</td>
-
-					<td>".$batch_cost."
-					</td>
-
-					<td>".
-						$row['creationdate']."
-					</td>
-
-					<td>".
-						$row['creationby']."
-					</td>
-
-				</tr>
-			";
-
-
-
-		}
-
-		$batch_details .= "</table>";
-
-		$customerInfo['batch_details'] = $batch_details;
-
-
-
-
-
-
-
-
-
-
-		$sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
-				$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
-				" WHERE planName = '".$planname."'";
-		$res = $dbSocket->query($sql);
-		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
-
-		$service_plan_info = "";
-		$service_plan_info = "<table $tableTags>";
-
-		foreach($row as $rowName => $rowValue) {
-
-			$service_plan_info .= "<tr $tableTrTags'>
-						<td>$rowName</td>
-						<td>$rowValue</td>
-						</tr>";
-
-		}
-
-		$service_plan_info .= "</table>";
-		$customerInfo['service_plan_info'] = $service_plan_info;
-
-
-		$sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
-					" WHERE name='".$hotspot_name."'";
-		$res = $dbSocket->query($sql);
-		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
-
-		$customerInfo['business_name'] = $row['name'];
-		$customerInfo['business_owner_name'] = $row['owner'];
-		$customerInfo['business_address'] = $row['address'];
-		$customerInfo['business_phone'] = $row['companyphone'];
-		$customerInfo['business_email'] = $row['companyemail'];
-		$customerInfo['business_web'] = $row['companywebsite'];
-
-
-
-
-
-
-		$batch_active_users = "";
-
-		$sql = "SELECT ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
-				$configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".
-
-				" FROM ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
-				$configValues['CONFIG_DB_TBL_RADACCT'].",".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
-
-				" WHERE ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
-				" AND ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
-				" AND ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
-				$configValues['CONFIG_DB_TBL_RADACCT'].".username".
-
-				" GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
-				" ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";
-
-		$res = $dbSocket->query($sql);
-
-		$batch_active_users = "<table $tableTags><tr $tableTrTags'>
-			<td>
-			".t('all','BatchName')."
-			</td>
-
-			<td>
-			".t('all','Username')."
-			</td>
-
-			<td>
-			".t('all','StartTime')."
-			</td>
-
-			</tr>";
-
-
-		$active_users_per = 0;
-		$total_users = 0;
-		$active_users = 0;
-		$batch_cost = 0;
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-
-			$username = $row['username'];
-			$acctstarttime = $row['acctstarttime'];
-			$batch_name = $row['batch_name'];
-
-			$batch_active_users .= "
-					<tr>
-
-					<td>".$batch_name."
-					</td>
-
-					<td>".$username."
-					</td>
-
-					<td>".$acctstarttime."
-					</td>
-
-				</tr>
-			";
-
-		}
-
-
-		$batch_active_users .= "</table>";
-		$customerInfo['batch_active_users'] = $batch_active_users;
-
-
-		require(dirname(__FILE__)."/../../library/closedb.php");
-
-		return $customerInfo;
-
-
-	}
+    include("../../library/checklogin.php");
+    $operator = $_SESSION['operator_user'];
+
+    $batch_name = (array_key_exists('batch_name', $_GET) && !empty(str_replace("%", "", trim($_GET['batch_name']))))
+                ? str_replace("%", "", trim($_GET['batch_name'])) : "";
+
+    $destination = (array_key_exists('destination', $_GET) && !empty(trim($_GET['destination'])) &&
+                    in_array(strtolower(trim($_GET['destination'])), array( "download", "email" )))
+                 ? strtolower($_GET['destination']) : "download";
+
+    if (empty($batch_name)) {
+        die("you should provide a valid batch name");
+    }
+    
+    include_once("../../notifications/processNotificationBatchDetails.php");
+    include_once("../../library/config_read.php");
+    
+    
+    function getBatchDetails($batch_name = NULL) {
+        global $configValues;
+
+        if ($batch_name == NULL || empty(trim($batch_name))) {
+            return false;
+        }
+
+        include("../../library/opendb.php");
+        include_once("../../lang/main.php");
+
+        $tableTags = 'style="width: 580px"';
+        $tableTrTags = 'style="background-color: #ECE5B6"';
+
+        $customerInfo = array();
+
+        $ths = array(
+                        t('all','BatchName'),
+                        t('all','HotSpot'),
+                        t('all','BatchStatus'),
+                        t('all','TotalUsers'),
+                        t('all','ActiveUsers'),
+                        t('all','PlanName'),
+                        t('all','PlanCost'),
+                        t('all','BatchCost'),
+                        t('all','CreationDate'),
+                        t('all','CreationBy'),
+                    );
+
+        // start filling in batch details
+        $batch_details = "<table $tableTags><tr $tableTrTags>";
+        
+        foreach ($ths as $th) {
+            $batch_details .= sprintf("<th>%s</th>", $th);
+        }
+        
+        $batch_details .= "</tr>";
+
+        $sql = sprintf("SELECT dbh.id AS batch_id, dbh.batch_name, dbh.batch_description, dbh.batch_status,
+                               COUNT(DISTINCT(ubi.id)) AS total_users, COUNT(DISTINCT(ra.username)) AS active_users,
+                               ubi.planname, dbp.plancost, dbp.plancurrency, dhs.name AS hotspot_name,
+                               dbh.creationdate, dbh.creationby, dbh.updatedate, dbh.updateby
+                          FROM %s AS dbh LEFT JOIN %s AS ubi ON dbh.id=ubi.batch_id
+                                        LEFT JOIN %s AS dbp ON dbp.planname=ubi.planname
+                                        LEFT JOIN %s AS dhs ON dbh.hotspot_id=dhs.id
+                                        LEFT JOIN %s AS ra ON ra.username=ubi.username
+                         WHERE dbh.batch_name='%s'
+                         GROUP BY dbh.batch_name", $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'],
+                                                   $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
+                                                   $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
+                                                   $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'],
+                                                   $configValues['CONFIG_DB_TBL_RADACCT'],
+                                                   $dbSocket->escapeSimple($batch_name));
+        $res = $dbSocket->query($sql);
+
+        
+        $active_users_per = 0;
+        $total_users = 0;
+        $active_users = 0;
+        $batch_cost = 0;
+
+        $hotspot_name = "";
+        $batch_id = "";
+        $planname = "";
+
+        while($row = $res->fetchRow()) {
+
+            foreach ($row as $i => $value) {
+                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
+            }
+
+            list(
+                    $batch_id, $batch_name, $batch_description, $batch_status, $total_users, $active_users, $planname,
+                    $plancost, $plancurrency, $hotspot_name, $creationdate, $creationby, $updatedate, $updateby
+                ) = $row;
+
+            
+            $batch_cost = (intval($active_users) * intval($plancost));
+
+            $tds = array(
+                            $batch_name,
+                            $hotspot_name,
+                            $batch_status,
+                            $total_users,
+                            $active_users,
+                            $planname,
+                            $plancost,
+                            $batch_cost,
+                            $creationdate,
+                            $creationby
+                        );
+                        
+            $batch_details .= "<tr>";
+            foreach ($tds as $td) {
+                $batch_details .= sprintf("<td>%s</td>", $td);
+            }
+            $batch_details .= "</tr>";
+
+        }
+
+        $batch_details .= "</table>";
+
+        $customerInfo['batch_details'] = $batch_details;
+
+
+        // filling in plan info
+        if (!empty($planname)) {
+        
+            $sql = sprintf("SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency
+                              FROM %s WHERE planName='%s'", $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
+                                                            $dbSocket->escapeSimple($planname));
+            $res = $dbSocket->query($sql);
+            $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
+            
+            echo $sql;
+            echo $row;
+            exit;
+
+            $service_plan_info = "<table $tableTags>";
+
+            foreach ($row as $rowName => $rowValue) {
+                $rowName = htmlspecialchars($rowName, ENT_QUOTES, 'UTF-8');
+                $rowValue = htmlspecialchars($rowValue, ENT_QUOTES, 'UTF-8');
+
+                $service_plan_info .= "<tr $tableTrTags>"
+                                    . sprintf("<th>%s</th>", $rowName)
+                                    . sprintf("<td>%s</td>", $rowValue)
+                                    . "</tr>";
+            }
+
+            $service_plan_info .= "</table>";
+            $customerInfo['service_plan_info'] = $service_plan_info;
+        }
+
+        // filling in business info
+        if (!empty($hotspot_name)) {
+            $sql = sprintf("SELECT id, name, owner, address, companyphone, companyemail, companywebsite
+                              FROM %s WHERE name='%s'", $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'],
+                                                        $dbSocket->escapeSimple($hotspot_name));
+            $res = $dbSocket->query($sql);
+            $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
+
+            $customerInfo['business_name'] = $row['name'];
+            $customerInfo['business_owner_name'] = $row['owner'];
+            $customerInfo['business_address'] = $row['address'];
+            $customerInfo['business_phone'] = $row['companyphone'];
+            $customerInfo['business_email'] = $row['companyemail'];
+            $customerInfo['business_web'] = $row['companywebsite'];
+        }
+
+        // active users details
+        $sql = sprintf("SELECT ubi.id, ubi.username, ra.acctstarttime, dbh.batch_name
+                          FROM %s AS ubi, %s AS ra, %s AS dbh
+                         WHERE ubi.batch_id=dbh.id
+                           AND ubi.batch_id='%s'
+                           AND ubi.username=ra.username
+                         GROUP BY ubi.username
+                         ORDER BY id, ra.radacctid ASC", $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
+                                                         $configValues['CONFIG_DB_TBL_RADACCT'],
+                                                         $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'],
+                                                         $dbSocket->escapeSimple($batch_id));
+        $res = $dbSocket->query($sql);
+
+        $ths = array(
+                        t('all','BatchName'),
+                        t('all','Username'),
+                        t('all','StartTime'),
+                    );
+
+        $batch_active_users = "<table $tableTags><tr $tableTrTags>";
+        foreach ($ths as $th) {
+            $batch_active_users .= sprintf("<th>%s</th>", $th);
+        }
+        $batch_active_users .= "</tr>";
+        
+        $active_users_per = 0;
+        $total_users = 0;
+        $active_users = 0;
+        $batch_cost = 0;
+        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+            foreach ($row as $i => $value) {
+                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
+            }
+
+            list($id, $username, $acctstarttime, $batch_name) = $row;
+
+            $tds = array( $username, $acctstarttime, $batch_name );
+
+            $batch_active_users .= "<tr>";
+            foreach ($tds as $td) {
+                $batch_active_users .= sprintf("<td>%s</td>", $td);
+            }
+            $batch_active_users .= "</tr>";
+        }
+        
+        $batch_active_users .= "</table>";
+        $customerInfo['batch_active_users'] = $batch_active_users;
+
+
+        require("../../library/closedb.php");
+
+        return $customerInfo;
+    }
+    
+    $customerInfo = getBatchDetails($batch_name);
+    
+    if ($customerInfo === false) {
+        die("error when loading batch details");
+    }
+
+    $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
+    $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
+    $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
+    $from = $configValues['CONFIG_MAIL_SMTPFROM'];
+
+    $pdfDocument = @createBatchDetailsNotification($customerInfo);
+
+    if ($destination == "download") {
+        $filename = sprintf('batch_notification_invoice_%s.pdf', date("Ymd"));
+        $size = strlen($pdfDocument);
+        
+        header("Content-type: application/pdf");
+        header(sprintf("Content-Disposition: attachment; filename=%s; size=%d", $filename, $size));
+        print $pdfDocument;
+        
+    } else if ($destination == "email") {
+        
+        @emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
+        
+        $redirect = (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE'])))
+                  ? trim($_SESSION['PREV_LIST_PAGE']) : "/mng-batch.php";
+        header("Location: " . $redirect);
+        
+    }
+    
 
 ?>

programming_language: PHP
filename: notificationsUserDetailsInvoice.php
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
@@ -15,473 +15,480 @@
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
 
-	require_once(dirname(__FILE__)."/../../notifications/processNotificationUserDetailsInvoice.php");
-	//require_once(dirname(__FILE__)."/../../library/config_read.php");
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/include/common/notificationsUserDetailsInvoice.php') !== false) {
+        header("Location: ../../index.php");
+        exit;
+    }
 
-	/*
-	isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
-	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";
-	
-	//test
-	$batch_name = "batch_test6"; 
-	
-	if ($batch_name != "") {
-		$customerInfo = @getBatchDetails($batch_name);
-		
-		$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
-		$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
-		$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
-		$from = $configValues['CONFIG_MAIL_SMTPFROM'];
-		
-		$pdfDocument = @createBatchDetailsNotification($customerInfo);
-		
-		if ($destination == "download") {
-			
-			header("Content-type: application/pdf");
-			header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
-			print $pdfDocument;
-			
-		} else if ($destination == "email") {
-			
-			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
-			header("Location: ".$_SERVER['HTTP_REFERER']);
-		}
-		
-	}
-	*/
-	
-	function getCustomerInfo($row) {
-	
-		//global $configValues;
-		
-		$customerInfo = array();
-		
+    require_once(dirname(__FILE__)."/../../notifications/processNotificationUserDetailsInvoice.php");
+    //require_once(dirname(__FILE__)."/../../library/config_read.php");
 
-		getCustomerInfo_customer_info($row, $customerInfo);
-		getCustomerInfo_service_plan($row, $customerInfo);
-		
-		return $customerInfo;
-		
-	}
-	
-	
-	
-	function getCustomerInfo_customer_info($row, &$customerInfo) {
-				
-		global $configValues;
-		require(dirname(__FILE__)."/../../lang/main.php");
-		
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-		
-		
-		if (!empty($row['email1']))
-			$invoice_email = $row['email1'];
-		else if (!empty($row['email2']))
-			$invoice_email = $row['email2'];
-		else if (!empty($row['email3']))
-			$invoice_email = $row['email3'];
-		else
-			$invoice_email = "";
-		
-		if (!empty($row['mobilephone']))
-			$invoice_phone = $row['mobilephone'];
-		else if (!empty($row['workphone']))
-			$invoice_phone = $row['mobilephone'];
-		else if (!empty($row['homephone']))
-			$invoice_phone = $row['homephone'];
-		else
-			$invoice_phone = "Unavailable";
-			
-		$invoice_address = "";
-		if (!empty($row['address']))
-			$invoice_address = $row['address'];
-		
-		if (!empty($row['city']))
-			$invoice_address .= ", ".$row['city'];
-		
-		if (!empty($row['state']))
-			$invoice_address .= "<br/>".$row['state'];
-		
-		if (!empty($row['zip']))
-			$invoice_address .= " ".$row['zip'];
-		
-		if (empty($invoice_address))
-			$invoice_address = "Unavailable";
-		
-		$customerInfo['business_name'] = $row['firstname']. " " .$row['lastname'];
-		$customerInfo['business_address'] = $invoice_address;
-		$customerInfo['business_phone'] = $invoice_phone;
-		$customerInfo['business_email'] = $invoice_email;
+    /*
+    isset($_GET['batch_name']) ? $batch_name = $_GET['batch_name'] : $batch_name = "";
+    isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "download";
+    
+    //test
+    $batch_name = "batch_test6"; 
+    
+    if ($batch_name != "") {
+        $customerInfo = @getBatchDetails($batch_name);
+        
+        $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
+        $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
+        $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
+        $from = $configValues['CONFIG_MAIL_SMTPFROM'];
+        
+        $pdfDocument = @createBatchDetailsNotification($customerInfo);
+        
+        if ($destination == "download") {
+            
+            header("Content-type: application/pdf");
+            header("Content-Disposition: attachment; filename=batch_notification_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
+            print $pdfDocument;
+            
+        } else if ($destination == "email") {
+            
+            @emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
+            header("Location: ".$_SERVER['HTTP_REFERER']);
+        }
+        
+    }
+    */
+    
+    function getCustomerInfo($row) {
+    
+        //global $configValues;
+        
+        $customerInfo = array();
+        
 
+        getCustomerInfo_customer_info($row, $customerInfo);
+        getCustomerInfo_service_plan($row, $customerInfo);
+        
+        return $customerInfo;
+        
+    }
+    
+    
+    
+    function getCustomerInfo_customer_info($row, &$customerInfo) {
+                
+        global $configValues;
+        require(dirname(__FILE__)."/../../lang/main.php");
+        
+        $tableTags = "width='580px' ";
+        $tableTrTags = "bgcolor='#ECE5B6'";
+        
+        
+        if (!empty($row['email1']))
+            $invoice_email = $row['email1'];
+        else if (!empty($row['email2']))
+            $invoice_email = $row['email2'];
+        else if (!empty($row['email3']))
+            $invoice_email = $row['email3'];
+        else
+            $invoice_email = "";
+        
+        if (!empty($row['mobilephone']))
+            $invoice_phone = $row['mobilephone'];
+        else if (!empty($row['workphone']))
+            $invoice_phone = $row['mobilephone'];
+        else if (!empty($row['homephone']))
+            $invoice_phone = $row['homephone'];
+        else
+            $invoice_phone = "Unavailable";
+            
+        $invoice_address = "";
+        if (!empty($row['address']))
+            $invoice_address = $row['address'];
+        
+        if (!empty($row['city']))
+            $invoice_address .= ", ".$row['city'];
+        
+        if (!empty($row['state']))
+            $invoice_address .= "<br/>".$row['state'];
+        
+        if (!empty($row['zip']))
+            $invoice_address .= " ".$row['zip'];
+        
+        if (empty($invoice_address))
+            $invoice_address = "Unavailable";
+        
+        $customerInfo['business_name'] = $row['firstname']. " " .$row['lastname'];
+        $customerInfo['business_address'] = $invoice_address;
+        $customerInfo['business_phone'] = $invoice_phone;
+        $customerInfo['business_email'] = $invoice_email;
 
-	}
-	
-	
-	function getCustomerInfo_service_plan($row, &$customerInfo) {
-				
-		global $configValues;
-		require(dirname(__FILE__)."/../../lang/main.php");
-		
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-		
-		$service_plan_info = "";
-		$service_plan_info = "<table $tableTags>";
-		
-		$service_plan_info .= "".
 
-					"<tr $tableTrTags'>
-					<td>".t('all','Username')."</td>
-					<td>".$row['username']."</td>
-					</tr>".
-		
-					"<tr $tableTrTags'>
-					<td>".t('all','PlanName')."</td>
-					<td>".$row['planname']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','PlanRecurring')."</td>
-					<td>".$row['planRecurring']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','PlanRecurringPeriod')."</td>
-					<td>".$row['planRecurringPeriod']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','PlanCost')."</td>
-					<td>".$row['planCost']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','NextBill')."</td>
-					<td>".$row['nextbill']."</td>
-					</tr>".
-					"<tr $tableTrTags'>
-					<td>".t('all','BillDue')."</td>
-					<td>".$row['billdue']."</td>
-					</tr>".
-					"";
-		
-		$service_plan_info .= "</table>";
-		$customerInfo['service_plan_info'] = $service_plan_info;	
-		
-	}
-	
-	
-	/*
-	function getBatchDetails($batch_name = NULL) {
-		
-		require(dirname(__FILE__)."/../../library/opendb.php");
-		require_once(dirname(__FILE__)."/../../lang/main.php");
-		
-		global $configValues;
-		
-		if ($batch_name == NULL || empty($batch_name))
-			exit;
-			
+    }
+    
+    
+    function getCustomerInfo_service_plan($row, &$customerInfo) {
+                
+        global $configValues;
+        require(dirname(__FILE__)."/../../lang/main.php");
+        
+        $tableTags = "width='580px' ";
+        $tableTrTags = "bgcolor='#ECE5B6'";
+        
+        $service_plan_info = "";
+        $service_plan_info = "<table $tableTags>";
+        
+        $service_plan_info .= "".
 
-			
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-		
-		$customerInfo = array();
-		
-		$sql = "SELECT ".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".
-			
-			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
-			"COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
-			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
-			$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
-			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".
-			
-			
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
-			$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
-			" FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','Username')."</td>
+                    <td>".$row['username']."</td>
+                    </tr>".
+        
+                    "<tr $tableTrTags'>
+                    <td>".t('all','PlanName')."</td>
+                    <td>".$row['planname']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','PlanRecurring')."</td>
+                    <td>".$row['planRecurring']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','PlanRecurringPeriod')."</td>
+                    <td>".$row['planRecurringPeriod']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','PlanCost')."</td>
+                    <td>".$row['planCost']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','NextBill')."</td>
+                    <td>".$row['nextbill']."</td>
+                    </tr>".
+                    "<tr $tableTrTags'>
+                    <td>".t('all','BillDue')."</td>
+                    <td>".$row['billdue']."</td>
+                    </tr>".
+                    "";
+        
+        $service_plan_info .= "</table>";
+        $customerInfo['service_plan_info'] = $service_plan_info;    
+        
+    }
+    
+    
+    /*
+    function getBatchDetails($batch_name = NULL) {
+        
+        require(dirname(__FILE__)."/../../library/opendb.php");
+        require_once(dirname(__FILE__)."/../../lang/main.php");
+        
+        global $configValues;
+        
+        if ($batch_name == NULL || empty($batch_name))
+            exit;
+            
 
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".
+            
+        $tableTags = "width='580px' ";
+        $tableTrTags = "bgcolor='#ECE5B6'";
+        
+        $customerInfo = array();
+        
+        $sql = "SELECT ".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_description,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_status,".
+            
+            "COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id)) as total_users,".
+            "COUNT(DISTINCT(".$configValues['CONFIG_DB_TBL_RADACCT'].".username)) as active_users,".
+            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname,".
+            $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancost,".
+            $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".plancurrency,".
+            $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".name as HotspotName,".
+            
+            
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationdate,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".creationby,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updatedate,".
+            $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".updateby ".
+            " FROM ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
+            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].
+            " ON ".
+            "(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id = ".
+            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id) ".
 
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
-			$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".
-			
-			" LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
-			" ON ".
-			"(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
-			$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".
-			
-			" WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '$batch_name' ".
-			" GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";
-			
-		$res = $dbSocket->query($sql);
+            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
+            " ON ".
+            "(".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].".planname = ".
+            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".planname) ".
 
-		$batch_details = "";
-		
-		$batch_details .= "<table $tableTags><tr $tableTrTags>
-					<td> 
-			".t('all','BatchName')."
-			</td>
-	
-			<td> 
-			".t('all','HotSpot')."
-			</td>
-	
-			<td> 
-			".t('all','BatchStatus')."
-			</td>
-			
-			<td> 
-			".t('all','TotalUsers')."
-			</td>
-	
-			<td> 
-			".t('all','ActiveUsers')."
-			</td>
-	
-			<td> 
-			".t('all','PlanName')."
-			</td>
-	
-			<td> 
-			".t('all','PlanCost')."
-			</td>
-	
-			<td> 
-			".t('all','BatchCost')."
-			</td>
-	
-			<td> 
-			".t('all','CreationDate')."
-			</td>
-	
-			<td> 
-			".t('all','CreationBy')."
-			</td>
-	
-			</tr>";
-		
-		$active_users_per = 0;
-		$total_users = 0;
-		$active_users = 0;
-		$batch_cost = 0;
-		
-		$hotspot_name = "";
-		$batch_id = "";
-		$planname = "";
-		
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-			
-			$batch_id = $row['id'];
-			$hotspot_name = $row['HotspotName'];
-			$batch_status = $row['batch_status'];
-			$plancost = $row['plancost'];
-			$total_users = $row['total_users'];
-			$active_users = $row['active_users'];
-			$batch_cost = ($active_users * $plancost);
-			$plan_currency = $row['plancurrency'];
-			$planname = $row['planname'];
-			
-	
-			$batch_details .= "
-					<tr>
-					
-					<td>".$row['batch_name']."
-						
-					</td>
-					
-					<td>".$hotspot_name."
-						
-					</td>
-			
-					<td>".$batch_status."
-						
-					</td>
-					
-					<td>".$total_users."
-						
-					</td>
-	
-					<td>".$active_users."
-						
-					</td>
-	
-					<td>".
-						$row['planname']."
-					</td>
-	
-					<td>".$plancost."
-					</td>
-	
-					<td>".$batch_cost."
-					</td>
-					
-					<td>".
-						$row['creationdate']."
-					</td>
-	
-					<td>".
-						$row['creationby']."
-					</td>
-	
-				</tr>
-			";
-			
+            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
+            " ON ".
+            "(".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".hotspot_id = ".
+            $configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].".id) ".
+            
+            " LEFT JOIN ".$configValues['CONFIG_DB_TBL_RADACCT'].
+            " ON ".
+            "(".$configValues['CONFIG_DB_TBL_RADACCT'].".username = ".
+            $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username) ".
+            
+            " WHERE ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name = '$batch_name' ".
+            " GROUP by ".$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ";
+            
+        $res = $dbSocket->query($sql);
 
-			
-		}
-		
-		$batch_details .= "</table>";
-		
-		$customerInfo['batch_details'] = $batch_details;
-		
-		
-		
-		
-		
-		
-		
-		
-		
-		
-		$sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
-				$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
-				" WHERE planName = '".$planname."'";
-		$res = $dbSocket->query($sql);
-		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
-		
-		$service_plan_info = "";
-		$service_plan_info = "<table $tableTags>";
-		
-		foreach($row as $rowName => $rowValue) {
-		
-			$service_plan_info .= "<tr $tableTrTags'>
-						<td>$rowName</td>
-						<td>$rowValue</td>
-						</tr>";
-		
-		}
-		
-		$service_plan_info .= "</table>";
-		$customerInfo['service_plan_info'] = $service_plan_info;
-		
-		
-		$sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
-					" WHERE name='".$hotspot_name."'";
-		$res = $dbSocket->query($sql);
-		$row = $res->fetchRow(DB_FETCHMODE_ASSOC);
-		
-		$customerInfo['business_name'] = $row['name'];
-		$customerInfo['business_owner_name'] = $row['owner'];
-		$customerInfo['business_address'] = $row['address'];
-		$customerInfo['business_phone'] = $row['companyphone'];
-		$customerInfo['business_email'] = $row['companyemail'];
-		$customerInfo['business_web'] = $row['companywebsite'];
-				
-		
-		
-		
-		
-		
-		$batch_active_users = "";
-		
-		$sql = "SELECT ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
-				$configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".
-				
-				" FROM ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
-				$configValues['CONFIG_DB_TBL_RADACCT'].",".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
-				
-				" WHERE ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
-				$configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
-				" AND ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
-				" AND ".
-				$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
-				$configValues['CONFIG_DB_TBL_RADACCT'].".username".
-	
-				" GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
-				" ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";
-				
-		$res = $dbSocket->query($sql);
-		
-		$batch_active_users = "<table $tableTags><tr $tableTrTags'>
-			<td> 
-			".t('all','BatchName')."
-			</td>
-	
-			<td> 
-			".t('all','Username')."
-			</td>
-	
-			<td> 
-			".t('all','StartTime')."
-			</td>
-	
-			</tr>";
+        $batch_details = "";
+        
+        $batch_details .= "<table $tableTags><tr $tableTrTags>
+                    <td> 
+            ".t('all','BatchName')."
+            </td>
+    
+            <td> 
+            ".t('all','HotSpot')."
+            </td>
+    
+            <td> 
+            ".t('all','BatchStatus')."
+            </td>
+            
+            <td> 
+            ".t('all','TotalUsers')."
+            </td>
+    
+            <td> 
+            ".t('all','ActiveUsers')."
+            </td>
+    
+            <td> 
+            ".t('all','PlanName')."
+            </td>
+    
+            <td> 
+            ".t('all','PlanCost')."
+            </td>
+    
+            <td> 
+            ".t('all','BatchCost')."
+            </td>
+    
+            <td> 
+            ".t('all','CreationDate')."
+            </td>
+    
+            <td> 
+            ".t('all','CreationBy')."
+            </td>
+    
+            </tr>";
+        
+        $active_users_per = 0;
+        $total_users = 0;
+        $active_users = 0;
+        $batch_cost = 0;
+        
+        $hotspot_name = "";
+        $batch_id = "";
+        $planname = "";
+        
+        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+            
+            $batch_id = $row['id'];
+            $hotspot_name = $row['HotspotName'];
+            $batch_status = $row['batch_status'];
+            $plancost = $row['plancost'];
+            $total_users = $row['total_users'];
+            $active_users = $row['active_users'];
+            $batch_cost = ($active_users * $plancost);
+            $plan_currency = $row['plancurrency'];
+            $planname = $row['planname'];
+            
+    
+            $batch_details .= "
+                    <tr>
+                    
+                    <td>".$row['batch_name']."
+                        
+                    </td>
+                    
+                    <td>".$hotspot_name."
+                        
+                    </td>
+            
+                    <td>".$batch_status."
+                        
+                    </td>
+                    
+                    <td>".$total_users."
+                        
+                    </td>
+    
+                    <td>".$active_users."
+                        
+                    </td>
+    
+                    <td>".
+                        $row['planname']."
+                    </td>
+    
+                    <td>".$plancost."
+                    </td>
+    
+                    <td>".$batch_cost."
+                    </td>
+                    
+                    <td>".
+                        $row['creationdate']."
+                    </td>
+    
+                    <td>".
+                        $row['creationby']."
+                    </td>
+    
+                </tr>
+            ";
+            
 
-			
-		$active_users_per = 0;
-		$total_users = 0;
-		$active_users = 0;
-		$batch_cost = 0;
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
-	
-			$username = $row['username'];
-			$acctstarttime = $row['acctstarttime'];
-			$batch_name = $row['batch_name'];
-			
-			$batch_active_users .= "
-					<tr>
-					
-					<td>".$batch_name."
-					</td>
-	
-					<td>".$username."
-					</td>
-	
-					<td>".$acctstarttime."
-					</td>
-	
-				</tr>
-			";
+            
+        }
+        
+        $batch_details .= "</table>";
+        
+        $customerInfo['batch_details'] = $batch_details;
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        $sql = "SELECT planId, planName, planRecurringPeriod, planCost, planSetupCost, planTax, planCurrency FROM ".
+                $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].
+                " WHERE planName = '".$planname."'";
+        $res = $dbSocket->query($sql);
+        $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
+        
+        $service_plan_info = "";
+        $service_plan_info = "<table $tableTags>";
+        
+        foreach($row as $rowName => $rowValue) {
+        
+            $service_plan_info .= "<tr $tableTrTags'>
+                        <td>$rowName</td>
+                        <td>$rowValue</td>
+                        </tr>";
+        
+        }
+        
+        $service_plan_info .= "</table>";
+        $customerInfo['service_plan_info'] = $service_plan_info;
+        
+        
+        $sql = "SELECT id, name, owner, address, companyphone, companyemail, companywebsite FROM ".$configValues['CONFIG_DB_TBL_DALOHOTSPOTS'].
+                    " WHERE name='".$hotspot_name."'";
+        $res = $dbSocket->query($sql);
+        $row = $res->fetchRow(DB_FETCHMODE_ASSOC);
+        
+        $customerInfo['business_name'] = $row['name'];
+        $customerInfo['business_owner_name'] = $row['owner'];
+        $customerInfo['business_address'] = $row['address'];
+        $customerInfo['business_phone'] = $row['companyphone'];
+        $customerInfo['business_email'] = $row['companyemail'];
+        $customerInfo['business_web'] = $row['companywebsite'];
+                
+        
+        
+        
+        
+        
+        $batch_active_users = "";
+        
+        $sql = "SELECT ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".id,".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username,".
+                $configValues['CONFIG_DB_TBL_RADACCT'].".acctstarttime,".
+                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".batch_name ".
+                
+                " FROM ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].",".
+                $configValues['CONFIG_DB_TBL_RADACCT'].",".
+                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].
+                
+                " WHERE ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = ".
+                $configValues['CONFIG_DB_TBL_DALOBATCHHISTORY'].".id".
+                " AND ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".batch_id = '$batch_id' ".
+                " AND ".
+                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username = ".
+                $configValues['CONFIG_DB_TBL_RADACCT'].".username".
+    
+                " GROUP by ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'].".username ".
+                " ORDER BY id ,".$configValues['CONFIG_DB_TBL_RADACCT'].".radacctid ASC ";
+                
+        $res = $dbSocket->query($sql);
+        
+        $batch_active_users = "<table $tableTags><tr $tableTrTags'>
+            <td> 
+            ".t('all','BatchName')."
+            </td>
+    
+            <td> 
+            ".t('all','Username')."
+            </td>
+    
+            <td> 
+            ".t('all','StartTime')."
+            </td>
+    
+            </tr>";
 
-		}
-		
+            
+        $active_users_per = 0;
+        $total_users = 0;
+        $active_users = 0;
+        $batch_cost = 0;
+        while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+    
+            $username = $row['username'];
+            $acctstarttime = $row['acctstarttime'];
+            $batch_name = $row['batch_name'];
+            
+            $batch_active_users .= "
+                    <tr>
+                    
+                    <td>".$batch_name."
+                    </td>
+    
+                    <td>".$username."
+                    </td>
+    
+                    <td>".$acctstarttime."
+                    </td>
+    
+                </tr>
+            ";
 
-		$batch_active_users .= "</table>";
-		$customerInfo['batch_active_users'] = $batch_active_users;
-		
-		
-		require(dirname(__FILE__)."/../../library/closedb.php");
-		
-		return $customerInfo;
-		
-		
-	}
-	*/
-?>
\ No newline at end of file
+        }
+        
+
+        $batch_active_users .= "</table>";
+        $customerInfo['batch_active_users'] = $batch_active_users;
+        
+        
+        require(dirname(__FILE__)."/../../library/closedb.php");
+        
+        return $customerInfo;
+        
+        
+    }
+    */
+?>

programming_language: PHP
filename: notificationsUserInvoice.php
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
@@ -15,190 +15,232 @@
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
 
-	require_once(dirname(__FILE__)."/../../notifications/processNotificationUserInvoice.php");
-	require_once(dirname(__FILE__)."/../../library/config_read.php");
-	
-	isset($_GET['invoice_id']) ? $invoice_id = $_GET['invoice_id'] : $invoice_id = "";
-	isset($_GET['destination']) ? $destination = $_GET['destination'] : $destination = "preview";
-	
-	if ($invoice_id != "") {
-		$customerInfo = @getInvoiceDetails($invoice_id);
-		$document = @createNotification($customerInfo, $destination == "preview");
-		
-		if ($destination == "download") {
-			
-			$pdfDocument = $document;
-			
-			header("Content-type: application/pdf");
-			header("Content-Disposition: attachment; filename=notification_user_invoice_" . date("Ymd") . ".pdf; size=" . strlen($pdfDocument));
-			print $pdfDocument;
-			
-		} else if ($destination == "email") {
-			
-			$pdfDocument = $document;
-			
-			$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
-			$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
-			$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
-			$from = $configValues['CONFIG_MAIL_SMTPFROM'];
-		
-			@emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from);
-			header("Location: ".$_SERVER['HTTP_REFERER']);
-			
-		} else if ($destination == 'preview') {
+    include("../../library/checklogin.php");
+    $operator = $_SESSION['operator_user'];
 
-			$htmlDocument = $document;
+    $invoice_id = (array_key_exists('invoice_id', $_GET) && !empty(str_replace("%", "", trim($_GET['invoice_id']))))
+                ? str_replace("%", "", trim($_GET['invoice_id'])) : "";
 
-			$result = file_put_contents(dirname(__FILE__).'/../../notifications/templates/invoice_preview.html', $htmlDocument);
-			header('Location: ../../notifications/templates/invoice_preview.html');
-		}
-	}
-	
-	
-	function getInvoiceDetails($invoice_id = NULL) {
-		
-		require(dirname(__FILE__)."/../../library/opendb.php");
-		require_once(dirname(__FILE__)."/../../lang/main.php");
-		
-		global $configValues;
-		
-		if ($invoice_id == NULL || empty($invoice_id))
-			exit;
-			
-		$tableTags = "width='580px' ";
-		$tableTrTags = "bgcolor='#ECE5B6'";
-		
-		// get invoice details
-		$sql = "SELECT a.id, a.date, a.status_id, a.type_id, a.user_id, a.notes, b.contactperson, b.username, b.company, ".
-				" b.city, b.state, b.country, b.zip, b.address, b.email, b.emailinvoice, b.phone, f.value as type, ".
-				" c.value AS status, COALESCE(e2.totalpayed, 0) as totalpayed, COALESCE(d2.totalbilled, 0) as totalbilled ".
-				" FROM ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICE']." AS a".
-				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO']." AS b ON (a.user_id = b.id) ".
-				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICESTATUS']." AS c ON (a.status_id = c.id) ".
-				" INNER JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICETYPE']." AS f ON (a.type_id = f.id) ".
-				" LEFT JOIN (SELECT SUM(d.amount + d.tax_amount) ".
-					" as totalbilled, invoice_id, amount, tax_amount, notes, plan_id FROM ".$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS']." AS d ".
-					" GROUP BY d.invoice_id) AS d2 ON (d2.invoice_id = a.id) ".
-				" LEFT JOIN ".$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS']." AS bp2 ON (bp2.id = d2.plan_id) ".
-				" LEFT JOIN (SELECT SUM(e.amount) as totalpayed, invoice_id FROM ". 
-				$configValues['CONFIG_DB_TBL_DALOPAYMENTS']." AS e GROUP BY e.invoice_id) AS e2 ON (e2.invoice_id = a.id) ".
-				" WHERE a.id = '".$dbSocket->escapeSimple($invoice_id)."'".
-				" GROUP BY a.id ";
-		$res = $dbSocket->query($sql);	
-		$invoiceDetails = $res->fetchRow(DB_FETCHMODE_ASSOC);
+    $destination = (array_key_exists('destination', $_GET) && !empty(trim($_GET['destination'])) &&
+                    in_array(strtolower(trim($_GET['destination'])), array( "download", "email", "preview" )))
+                 ? strtolower($_GET['destination']) : "preview";
+                 
+    if (empty($invoice_id)) {
+        die("you should provide a valid invoice id");
+    }
 
-		if (empty($invoiceDetails['email']))
-			$customer_email = $invoiceDetails['emailinvoice'];
-		else
-			$customer_email = $invoiceDetails['email'];
-			
-		// populate user contact informatin
-		$customerInfo['customer_name'] = $invoiceDetails['contactperson'];
-		$customerInfo['customer_address'] = $invoiceDetails['address']. " " . $invoiceDetails['city']. " " . $invoiceDetails['state'];
-		$customerInfo['customer_email'] = $customer_email;
-		$customerInfo['customer_phone'] = $invoiceDetails['phone'];
-		
-		// populate user invoice details
-		$balance = (float) ($invoiceDetails['totalpayed'] - $invoiceDetails['totalbilled']);
-		$invoice_details = "";
-		$invoice_details .= "".
-		"<b>".t('all','ClientName')."</b>: ".$invoiceDetails['contactperson']."<br/>".
-		"<b>".t('all','Invoice')."</b>: ".$invoice_id."<br/>".
-		"<b>".t('all','Date')."</b>: ".$invoiceDetails['date']."<br/>".
-		"<b>".t('all','TotalBilled')."</b>: ".$invoiceDetails['totalbilled']."<br/>".
-		"<b>".t('all','TotalPayed')."</b>: ".$invoiceDetails['totalpayed']."<br/>".
-		"<b>".t('all','Balance')."</b>: ".$balance."<br/>".
-		"<b>".t('all','Status')."</b>: ".$invoiceDetails['status']."<br/>".
-		"<b>".t('ContactInfo','Notes')."</b>: ".$invoiceDetails['notes']."<br/><br/><br/>";
-		
-		$customerInfo['invoice_details'] = $invoice_details;
-		
-		// populate customer data - NEW STYLE
-		$customerInfo['customerId'] = $invoiceDetails['user_id'];
-		$customerInfo['customerName'] = (isset($invoiceDetails['company']) ? $invoiceDetails['company'] : $invoiceDetails['contactperson']);
-		$customerInfo['customerAddress'] = $invoiceDetails['address'];
-		$customerInfo['customerAddress2'] = $invoiceDetails['zip'] . ' '. $invoiceDetails['city'] . ' ' .
-		                                    $invoiceDetails['state'] . ' ' . $invoiceDetails['country'];
-		$customerInfo['customerEmail'] = $invoiceDetails['email'];
-		$customerInfo['customerPhone'] = $invoiceDetails['phone'];
-		$customerInfo['customerContact'] = $invoiceDetails['contactperson'];
-		
-		$customerInfo['invoiceNumber'] = $invoice_id;
-		$customerInfo['invoiceDate'] = date('Y-m-d', strtotime($invoiceDetails['date']));
-		$customerInfo['invoiceStatus'] = strtoupper($invoiceDetails['status']);
-		$customerInfo['invoiceTotalBilled'] = $invoiceDetails['totalbilled'];
-		$customerInfo['invoicePaid'] = $invoiceDetails['totalpayed'];
-		$customerInfo['invoiceDue'] = $balance;
-		$customerInfo['invoiceNotes'] = $invoiceDetails['notes'];
-		
-		// populate user invoice items
-		$invoice_items = "";
-		$invoice_items .= "<table $tableTags><tr $tableTrTags>
-			<th>Plan</th>
-			<th>Item Amount</th>
-			<th>Item Tax</th>
-			<th>Notes</th>
-			</tr>
-			";
+    require_once("../../notifications/processNotificationUserInvoice.php");
+    require_once("../../library/config_read.php");
+    
+    $customerInfo = @getInvoiceDetails($invoice_id);
+    
+    if ($customerInfo === false) {
+        die("error when loading invoice");
+    }
+    
+    $document = @createNotification($customerInfo, $destination == "preview");
+        
+    if ($destination == "download") {
+        $filename = sprintf('notification_invoice_%s.pdf', date("Ymd"));
+        $size = strlen($document);
+        
+        header("Content-type: application/pdf");
+        header(sprintf("Content-Disposition: attachment; filename=%s; size=%d", $filename, $size));
+            
+        print $document;
 
-		// get all invoice items
-		$sql = 'SELECT a.id, a.plan_id, a.amount, a.tax_amount, a.notes, b.planName '.
-				' FROM '.$configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'].' a '.
-				' LEFT JOIN '.$configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'].' b ON a.plan_id = b.id '.
-				' WHERE a.invoice_id = '.$invoice_id.' ORDER BY a.id ASC';
-		$res = $dbSocket->query($sql);
-		$logDebugSQL .= $sql . "\n";
-		
-		// initialize invoice items - NEW STYLE
-		$invoiceItems = array();
-		$invoiceItemsNumber = 1;
-		$invoiceItemsTotalAmount = 0;
-		$invoiceItemsTotalTax = 0;
-		
-		while($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
+    } else if ($destination == "email") {
+        $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
+        $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
+        $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
+        
+        $from = $configValues['CONFIG_MAIL_SMTPFROM'];
+        
+        @emailNotification($document, $customerInfo, $smtpInfo, $from);
+        $redirect = (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE'])))
+                  ? trim($_SESSION['PREV_LIST_PAGE']) : "/bill-invoice.php";
+        header("Location: " . $redirect);
+            
+    } else /*if ($destination == "preview")*/ {
+        print $document;
+        //~ $result = file_put_contents(dirname(__FILE__).'/../../notifications/templates/invoice_preview.html', $htmlDocument);
+        //~ header('Location: ../../notifications/templates/invoice_preview.html');
+    }
+    
+    
+    
+    function getInvoiceDetails($invoice_id = NULL) {
+        global $configValues;
+        
+        if ($invoice_id == NULL || empty($invoice_id)) {
+            exit;
+        }
+        
+        include("../../library/opendb.php");
+        include_once("../../lang/main.php");
+            
+        $tableTags = 'style="width: 580px"';
+        $tableTrTags = 'style="background-color: #ECE5B6"';
+        
+        // get invoice details
+        $sql = sprintf("SELECT a.id, a.date, a.status_id, a.type_id, a.user_id, a.notes, b.contactperson, b.username,
+                               b.company, b.city, b.state, b.country, b.zip, b.address, b.email, b.emailinvoice, b.phone,
+                               f.value AS type, c.value AS status, COALESCE(e2.totalpayed, 0) AS totalpayed,
+                               COALESCE(d2.totalbilled, 0) AS totalbilled
+                          FROM %s AS a INNER JOIN %s AS b ON a.user_id=b.id
+                                       INNER JOIN %s AS c ON a.status_id=c.id
+                                       INNER JOIN %s AS f ON a.type_id=f.id
+                                       LEFT JOIN (
+                                                    SELECT SUM(d.amount + d.tax_amount) AS totalbilled, invoice_id,
+                                                           amount, tax_amount, notes, plan_id
+                                                      FROM %s AS d
+                                                     GROUP BY d.invoice_id
+                                                 ) AS d2 ON d2.invoice_id=a.id
+                                       LEFT JOIN %s AS bp2 ON bp2.id=d2.plan_id
+                                       LEFT JOIN (
+                                                    SELECT SUM(e.amount) AS totalpayed, invoice_id
+                                                      FROM %s AS e
+                                                     GROUP BY e.invoice_id
+                                                 ) AS e2 ON e2.invoice_id=a.id
+                         WHERE a.id=%d
+                         GROUP BY a.id", $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICE'],
+                                         $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
+                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICESTATUS'],
+                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICETYPE'],
+                                         $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'],
+                                         $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
+                                         $configValues['CONFIG_DB_TBL_DALOPAYMENTS'],
+                                         $dbSocket->escapeSimple($invoice_id));
+        $res = $dbSocket->query($sql);    
+        $invoiceDetails = $res->fetchRow(DB_FETCHMODE_ASSOC);
 
-			$invoice_items .= "". 
-				"<tr>".
-					"<td>".$row['planName']."</td>".
-					"<td>".$row['amount']."</td>".
-					"<td>".$row['tax_amount']."</td>".
-					"<td>".$row['notes']."</td>".
-				"</tr>";
-				
-			// populate invoice items - NEW STYLE
-			$invoiceItem = array();
-			
-			$invoiceItem['invoiceItemNumber'] = sprintf('%02d', $invoiceItemsNumber);
-			$invoiceItem['invoiceItemPlan'] = $row['planName'];
-			$invoiceItem['invoiceItemNotes'] = $row['notes'];
-			$invoiceItem['invoiceItemAmount'] = $row['amount'];
-			$invoiceItem['invoiceItemTaxAmount'] = $row['tax_amount'];
-			$invoiceItem['invoiceItemTotalAmount'] = $row['amount'] + $row['tax_amount'];
-			
-			$invoiceItems[] = $invoiceItem;
-			$invoiceItemsTotalAmount += $row['amount'];
-			$invoiceItemsTotalTax += $row['tax_amount'];
-			
-			++$invoiceItemsNumber;
-		}
+        $customer_email = (empty($invoiceDetails['email'])) ? $invoiceDetails['emailinvoice'] : $invoiceDetails['email'];
+            
+        // populate user contact informatin
+        $customerInfo['customer_name'] = $invoiceDetails['contactperson'];
+        $customerInfo['customer_address'] = $invoiceDetails['address'] . " " . $invoiceDetails['city'] . " " . $invoiceDetails['state'];
+        $customerInfo['customer_email'] = $customer_email;
+        $customerInfo['customer_phone'] = $invoiceDetails['phone'];
+        
+        // populate user invoice details
+        $balance = floatval($invoiceDetails['totalpayed'] - $invoiceDetails['totalbilled']);
+        
+        $details = array(
+                            array( t('all','ClientName'), $invoiceDetails['contactperson'] ),
+                            array( t('all','Invoice'), $invoice_id ), 
+                            array( t('all','Date'), $invoiceDetails['date'] ), 
+                            array( t('all','TotalBilled'), $invoiceDetails['totalbilled'] ), 
+                            array( t('all','TotalPayed'), $invoiceDetails['totalpayed'] ), 
+                            array( t('all','Balance'), $balance ), 
+                            array( t('all','Status'), $invoiceDetails['status'] ), 
+                            array( t('ContactInfo','Notes'), $invoiceDetails['notes'] ), 
+                        );
+        
+        $invoice_details = "";
+        foreach ($details as $detail) {
+            list( $caption, $data ) = $detail;
+            $invoice_details .= sprintf("<b>%s</b>; %s<br>", $caption, $data);
+        }
+        $invoice_details .= "<br><br>";
 
-		$invoice_items .= "</table>";
-		
-		$customerInfo['invoice_items'] = $invoice_items;
-		
-		// populate invoice items - NEW STYLE
-		$customerInfo['invoiceItems'] = $invoiceItems;
-		$customerInfo['invoiceTotalAmount'] = $invoiceItemsTotalAmount;
-		$customerInfo['invoiceTotalTax'] = $invoiceItemsTotalTax;
-		
-		require(dirname(__FILE__)."/../../library/closedb.php");
+        $customerInfo['invoice_details'] = $invoice_details;
+        
+        // populate customer data - NEW STYLE
+        $customerInfo['customerId'] = $invoiceDetails['user_id'];
+        $customerInfo['customerName'] = (isset($invoiceDetails['company']) ? $invoiceDetails['company'] : $invoiceDetails['contactperson']);
+        $customerInfo['customerAddress'] = $invoiceDetails['address'];
+        $customerInfo['customerAddress2'] = $invoiceDetails['zip'] . ' '. $invoiceDetails['city'] . ' ' .
+                                            $invoiceDetails['state'] . ' ' . $invoiceDetails['country'];
+        $customerInfo['customerEmail'] = $invoiceDetails['email'];
+        $customerInfo['customerPhone'] = $invoiceDetails['phone'];
+        $customerInfo['customerContact'] = $invoiceDetails['contactperson'];
+        
+        $customerInfo['invoiceNumber'] = $invoice_id;
+        $customerInfo['invoiceDate'] = date('Y-m-d', strtotime($invoiceDetails['date']));
+        $customerInfo['invoiceStatus'] = strtoupper($invoiceDetails['status']);
+        $customerInfo['invoiceTotalBilled'] = $invoiceDetails['totalbilled'];
+        $customerInfo['invoicePaid'] = $invoiceDetails['totalpayed'];
+        $customerInfo['invoiceDue'] = $balance;
+        $customerInfo['invoiceNotes'] = $invoiceDetails['notes'];
 
-		return $customerInfo;
-	}
-	
+        $ths = array(
+                        t('title','Plan']),
+                        t('all','Tax']),
+                        t('all','Amount']),
+                        t('ContactInfo','Notes']),
+                    );
+        // populate user invoice items
+        $invoice_items = "<table $tableTags><tr $tableTrTags>";
+        foreach ($ths as $th) {
+            $invoice_items .= sprintf("<th>%s</th>", $th);
+        }
+        $invoice_items .= "</tr>";
+        
+        // get all invoice items
+        $sql = sprintf("SELECT a.id, a.plan_id, a.amount, a.tax_amount, a.notes, b.planName
+                          FROM %s a LEFT JOIN %s b ON a.plan_id=b.id
+                         WHERE a.invoice_id=%d
+                         ORDER BY a.id ASC", $configValues['CONFIG_DB_TBL_DALOBILLINGINVOICEITEMS'],
+                                             $configValues['CONFIG_DB_TBL_DALOBILLINGPLANS'],
+                                             $dbSocket->escapeSimple($invoice_id));
+        $res = $dbSocket->query($sql);
+        
+        // initialize invoice items - NEW STYLE
+        $invoiceItems = array();
+        $invoiceItemsNumber = 1;
+        $invoiceItemsTotalAmount = 0;
+        $invoiceItemsTotalTax = 0;
+        
+        while ($row = $res->fetchRow()) {
+            foreach ($row as $i => $value) {
+                $row[$i] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
+            }
+
+            list($id, $plan_id, $amount, $tax_amount, $notes, $planName) = $row;
+
+            $tds = array( $planName, $amount, $tax_amount, $notes );
+            
+            $invoice_items .= "<tr>";
+            foreach ($tds as $td) {
+                $invoice_items .= sprintf("<td>%s</td>", $td);
+            }
+            $invoice_items .= "</tr>";
+
+            // populate invoice items - NEW STYLE
+            $invoiceItem = array();
+            
+            $invoiceItem['invoiceItemNumber'] = sprintf('%02d', $invoiceItemsNumber);
+            $invoiceItem['invoiceItemPlan'] = $planName;
+            $invoiceItem['invoiceItemNotes'] = $notes;
+            $invoiceItem['invoiceItemAmount'] = $amount;
+            $invoiceItem['invoiceItemTaxAmount'] = $tax_amount;
+            $invoiceItem['invoiceItemTotalAmount'] = intval($amount) + intval($tax_amount);
+            
+            $invoiceItems[] = $invoiceItem;
+            $invoiceItemsTotalAmount += intval($amount);
+            $invoiceItemsTotalTax += intval($tax_amount);
+            
+            ++$invoiceItemsNumber;
+        }
+
+        $invoice_items .= "</table>";
+        
+        $customerInfo['invoice_items'] = $invoice_items;
+        
+        // populate invoice items - NEW STYLE
+        $customerInfo['invoiceItems'] = $invoiceItems;
+        $customerInfo['invoiceTotalAmount'] = $invoiceItemsTotalAmount;
+        $customerInfo['invoiceTotalTax'] = $invoiceItemsTotalTax;
+        
+        include("../../library/closedb.php");
+
+        return $customerInfo;
+    }
+    
 ?>

programming_language: PHP
filename: notificationsWelcome.php
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
@@ -15,60 +15,82 @@
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
+ 
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/include/common/notificationWelcome.php') !== false) {
+        header("Location: ../../index.php");
+        exit;
+    }
+ 
+    include_once("notifications/processNotificationWelcome.php");
+    
+    // we (try to) init the email
+    if (isset($email) && !empty($email)) {
+        $invoice_email = $email;
+    } else if (isset($bi_emailinvoice) && !empty($bi_emailinvoice)) {
+        $invoice_email = $bi_emailinvoice;
+    } else if (isset($bi_email) && !empty($bi_email)) {
+        $invoice_email = $bi_email;
+    } else {
+        $invoice_email = "";
+    }
+    
+    
+    // we (try to) init the phone
+    if (isset($mobilephone) && !empty($mobilephone)) {
+        $invoice_phone = $mobilephone;
+    } else if (isset($workphone) && !empty($workphone)) {
+        $invoice_phone = $workphone;
+    } else if (isset($homephone) && !empty($homephone)) {
+        $invoice_phone = $homephone;
+    } else {
+        $invoice_phone = "(n/a)";
+    }
+    
+    // we also (try to) init the address
+    $invoice_address = "";
+    if (isset($ui_address) &&!empty($ui_address)) {
+        $invoice_address = $ui_address;
+    }
+    
+    if (isset($ui_city) && !empty($ui_city)) {
+        $invoice_address .= ", " . $ui_city;
+    }
+    
+    if (isset($ui_state) && !empty($ui_state)) {
+        $invoice_address .= "<br>" . $ui_state;
+    }
+    
+    if (isset($ui_zip) && !empty($ui_zip)) {
+        $invoice_address .= " " . $ui_zip;
+    }
+    
+    if (empty($invoice_address)) {
+        $invoice_address = "(n/a)";
+    }
+    
+    // set SMTP server info
+    $smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
+    $smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
+    $smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
+    
+    // set the from field
+    $from = $configValues['CONFIG_MAIL_SMTPFROM'];
+    
+    // set customer info
+    $customerInfo = array();
+    $customerInfo['customer_name'] = sprintf("%s %s", $firstname, $lastname);
+    $customerInfo['customer_address'] = $invoice_address;
+    $customerInfo['customer_phone'] = $invoice_phone;
+    $customerInfo['customer_email'] = $invoice_email;
+    
+    $customerInfo['service_plan_name'] = $planName;
+    
+    @sendWelcomeNotification($customerInfo, $smtpInfo, $from);
 
-	require_once(dirname(__FILE__)."/../../notifications/processNotificationWelcome.php");
-	
-	if (!empty($email))
-		$invoice_email = $email;
-	else if (!empty($bi_emailinvoice))
-		$invoice_email = $bi_emailinvoice;
-	else if (!empty($bi_email))
-		$invoice_email = $bi_email;
-	else
-		$invoice_email = "";
-	
-	if (!empty($mobilephone))
-		$invoice_phone = $mobilephone;
-	else if (!empty($workphone))
-		$invoice_phone = $workphone;
-	else if (!empty($homephone))
-		$invoice_phone = $homephone;
-	else
-		$invoice_phone = "Unavailable";
-		
-	$invoice_address = "";
-	if (!empty($ui_address))
-		$invoice_address = $ui_address;
-	
-	if (!empty($ui_city))
-		$invoice_address .= ", ".$ui_city;
-	
-	if (!empty($ui_state))
-		$invoice_address .= "<br/>".$ui_state;
-	
-	if (!empty($ui_zip))
-		$invoice_address .= " ".$ui_zip;
-	
-	if (empty($invoice_address))
-		$invoice_address = "Unavailable";
-	
-	$customerInfo = array();
-	$customerInfo['customer_name'] = $firstname ." ".$lastname;
-	$customerInfo['customer_address'] = $invoice_address;
-	
-	$customerInfo['customer_phone'] = $invoice_phone;
-	$customerInfo['customer_email'] = $invoice_email;
-	$customerInfo['service_plan_name'] = $planName;
-	
-	$smtpInfo['host'] = $configValues['CONFIG_MAIL_SMTPADDR'];
-	$smtpInfo['port'] = $configValues['CONFIG_MAIL_SMTPPORT'];
-	$smtpInfo['auth'] = $configValues['CONFIG_MAIL_SMTPAUTH'];
-	$from = $configValues['CONFIG_MAIL_SMTPFROM'];
-	
-	@sendWelcomeNotification($customerInfo, $smtpInfo, $from);
-
-?>
\ No newline at end of file
+?>

programming_language: PHP
filename: printTickets.php
change_type: ModificationType.MODIFY
diff: @@ -145,8 +145,7 @@
 
 }
 
-    $ticketInformation = "Information: To use this card, please connect your device to the nearest ssid."
-                       . "<br>"
+    $ticketInformation = "<strong>Information</strong>:<br>to use this card, please connect your device to the nearest ssid."
                        . "Open your web browser and enter each needed field.";
     $ticketLogoFile = "/images/daloradius_small.png";
 
@@ -155,16 +154,22 @@
         if (array_key_exists('accounts', $_POST) && !empty($_POST['accounts']) && is_array($_POST['accounts']) &&
             array_key_exists('type', $_POST) && $_POST['type'] == "batch") {
             
+            $batch_name = (array_key_exists('batch_name', $_POST) && !empty(trim($_POST['batch_name'])))
+                        ? htmlspecialchars(trim($_POST['batch_name']), ENT_QUOTES, 'UTF-8') : "";
+            
             $accounts = $_POST['accounts'];
             
             if (array_key_exists('ticketInformation', $_POST) && !empty(trim($_POST['ticketInformation']))) {
-                $ticketInformation = htmlspecialchars(trim($_POST['ticketInformation']), ENT_QUOTES, 'UTF-8');
+                $ticketInformation = "<strong>Information</strong>:<br>" . htmlspecialchars(trim($_POST['ticketInformation']), ENT_QUOTES, 'UTF-8');
                 $ticketInformation = str_replace("\n", "<br>", $ticketInformation);
             }
             
             $plan = (array_key_exists('plan', $_POST) && !empty(trim($_POST['plan'])))
                   ? trim($_POST['plan']) : "";
-                  
+            
+            $ticketCost = "";
+            $ticketTime = "";
+            
             if (!empty($plan)) {
                 include_once('../../library/opendb.php');
                 include_once('../management/pages_common.php');
@@ -179,8 +184,17 @@
                 $ticketTime = time2str($ticketTime);
 
                 include_once('../../library/closedb.php');
-            } else {
-                $ticketCost = $ticketTime = $ticketCurrency = "(n/a)";
+            }
+            
+            $card_body_height = 10;
+            $card_foot_height = 30;
+            if (!empty($ticketCost)) {
+                $card_foot_height -= 5;
+                $card_body_height += 5;
+            }
+            if (!empty($ticketTime)) {
+                $card_foot_height -= 5;
+                $card_body_height += 5;
             }
             
 ?>
@@ -188,10 +202,20 @@
 <html lang="en">
     <head>
         <meta charset="utf-8" />
-        <title>untitled</title>
+        <title><?= (!empty($batch_name)) ? $batch_name : "user cards" ?></title>
         
         <style>
 
+@page {
+    size: 21cm 29.7cm;
+    margin: 0;
+}
+
+body {
+    font-family: Tahoma;
+    padding: 1cm;
+}
+
 .container:first-child .card {
     border-top: 1px dotted gray;
 }
@@ -229,8 +253,8 @@
 }
 
 .card-body {
-    height: 18mm;
-    width: 100%;
+    height: <?= $card_body_height ?>mm;
+    width: 42mm;
     margin: 0;
     padding: 1mm;
 }
@@ -240,25 +264,40 @@
     margin: 0;
     width: 100%;
     text-align: center;
-    font-size: 9pt;
+    font-size: 8pt;
     padding: 0;
 }
 
-.card-foot {
-    height: 24mm;
+.card-body table tr {
+    border: 0;
+    margin: 0;
     width: 100%;
+    height: 5mm;
+    padding: 0;
+}
+
+.card-body table th {
+    text-align: right;
+}
+
+.card-body table td {
+    text-align: left;
+}
+
+.card-foot {
+    height: <?= $card_foot_height ?>mm;
     margin: 0;
 }
 
 .card-foot p {
     margin: 0;
-    padding: 2mm;
-    font-size: 7pt;
+    width: 42mm;
+    padding: 1mm;
+    font-size: 8pt;
     font-weight: normal;
     text-align: justify;
 }
 
-
 .container {
     text-align: center;
     padding: 0;
@@ -291,27 +330,29 @@
                 echo '</div><div class="container">';
             }
             
+            $trs = array(
+                            "User" => $username,
+                            "Pass" => $password
+                        );
+            
+            if (!empty($ticketTime)) {
+                $trs["Validity"] = $ticketTime;
+            }
+            
+            if (!empty($ticketCost)) {
+                $trs["Price"] = $ticketCost;
+            }
+            
             echo '<div class="card">';
             printf('<div class="card-head"><img src="%s"></div>', $ticketLogoFile); 
             echo '<div class="card-body">'
-               . '<table>'
-               . '<tr>'
-               . '<th>Login</th>';
-            printf('<td>%s</td>', htmlspecialchars($username, ENT_QUOTES, 'UTF-8'));
-            echo '</tr>'
-               . '<tr>'
-               . '<th>Password</th>';
-            printf('<td>%s</td>', htmlspecialchars($password, ENT_QUOTES, 'UTF-8'));
-            echo '</tr>'
-               . '<tr>'
-               . '<th>Validity</th>';
-            printf('<td>%s</td>', htmlspecialchars($ticketTime, ENT_QUOTES, 'UTF-8'));
-            echo '</tr>'
-               . '<tr>'
-               . '<th>Price</th>';
-            printf('<td>%s</td>', htmlspecialchars($ticketCost, ENT_QUOTES, 'UTF-8'));
-            echo '</tr>'
-               . '</table>'
+               . '<table>';
+
+            foreach ($trs as $label => $value) {
+                printf('<tr><th>%s:</th><td>%s</td></tr>', $label, htmlspecialchars($value, ENT_QUOTES, 'UTF-8'));
+            }
+
+            echo '</table>'
                . '</div>'
                . '<div class="card-foot">';
             printf('<p>%s</p>', $ticketInformation);

programming_language: PHP
filename: checklogin.php
change_type: ModificationType.MODIFY
diff: @@ -33,9 +33,15 @@ if (strpos($_SERVER['PHP_SELF'], '/library/checklogin.php') !== false) {
 include('sessions.php');
 dalo_session_start();
 
+
 if (!array_key_exists('daloradius_logged_in', $_SESSION) || $_SESSION['daloradius_logged_in'] !== true) {
     $_SESSION['daloradius_logged_in'] = false;
-    header('Location: /login.php');
+
+    // implement a sort of "dynamic redirect finder" based on the number of "/" in the PHP_SELF value
+    $count = substr_count($_SERVER['PHP_SELF'], "/", 1);
+    $location = str_repeat("../", $count) . "login.php";
+
+    header("Location: " . $location);
     exit;
 }
 

programming_language: PHP
filename: mng-batch-add.php
change_type: ModificationType.MODIFY
diff: @@ -398,11 +398,11 @@
                     } // end for
                     
                     $form_id = "export-users-form";
-                    $exportForm .= sprintf('<form target="_blank" style="display: none" id="%s" ', $form_id)
+                    $exportForm .= sprintf('<form target="_blank" id="%s" ', $form_id)
                                  . 'method="POST">'
-                                 . sprintf('<input type="hidden" name="batch_name" value="%s">',
+                                 . sprintf('<input style="display: none" type="hidden" name="batch_name" value="%s">',
                                            htmlspecialchars($batch_name, ENT_QUOTES, 'UTF-8'))
-                                 . '<input type="hidden" name="type" value="batch">';
+                                 . '<input style="display: none" type="hidden" name="type" value="batch">';
                                  
                     if (!empty($planName)) {
                         $exportForm .= sprintf('<input type="hidden" name="plan" value="%s">',
@@ -412,30 +412,25 @@
                     for ($i = 0; $i < count($inserted_usernames); $i++) {
                         $u = $inserted_usernames[$i];
                         $p = $inserted_passwords[$i];
-                        $exportForm .= sprintf('<input type="hidden" name="accounts[%d][0]" value="%s">', $i, htmlspecialchars($u, ENT_QUOTES, 'UTF-8'))
-                                     . sprintf('<input type="hidden" name="accounts[%d][1]" value="%s">', $i, htmlspecialchars($p, ENT_QUOTES, 'UTF-8'));
+                        $exportForm .= sprintf('<input style="display: none" type="hidden" name="accounts[%d][0]" value="%s">', $i, htmlspecialchars($u, ENT_QUOTES, 'UTF-8'))
+                                     . sprintf('<input style="display: none" type="hidden" name="accounts[%d][1]" value="%s">', $i, htmlspecialchars($p, ENT_QUOTES, 'UTF-8'));
                     }
-                    $exportForm .= '</form>';
+                    
+                    
+                    $exportForm .= '<fieldset><label for="ticketInformation" class="form">Description</label>'
+                                 . '<textarea class="form" id="ticketInformation" name="ticketInformation">'
+                                 . 'to use this card, please connect your device to the nearest ssid.' . "\n"
+                                 . 'Open your web browser and enter each needed field.</textarea>';
+                    $exportForm .= '</fieldset></form>';
                     $onclick = "batch_export('include/common/fileExportCSV.php')";
-                    $exportForm .= sprintf('<input type="button" onclick="%s" value="CSV Download">', $onclick);
+                    $exportForm .= sprintf('<input style="margin: 10px" type="button" onclick="%s" value="CSV Download">', $onclick);
                     $onclick = "batch_export('include/common/printTickets.php')";
-                    $exportForm .= sprintf('<input type="button" onclick="%s" value="Printable Tickets">', $onclick);
+                    $exportForm .= sprintf('<input style="margin: 10px" type="button" onclick="%s" value="Printable Tickets">', $onclick);
                             
-                            // if batch_history record was created successfuly
-                            //~ if ($sql_batch_id != 0) {
-                                //~ // remove the last || chars to sanitize it for proper format
-                                //~ $exportCSV = substr($exportCSV, 0, -2);
-                                //~ $successMsg = "Exported Usernames - ".
-                                                    //~ "<a href='include/common/fileExportCSV.php?csv_output=$exportCSV'>download</a><br/>".
-                                                //~ "Printable Tickets - ".
-                                                    //~ "<a href='include/common/printTickets.php?type=batch&plan=$planName&accounts=$exportCSV'>view</a><br/>".
-                                                //~ "Added to database new user(s): <b> $actionMsgGoodUsernames </b><br/>";
                     $successMsg = sprintf("Created %d user(s) (batch name: <strong>%s</strong>)", count($inserted_usernames)-1, $batch_name);
                     $logAction .= sprintf("Successfully added to database new users [%s] with prefix [%s] on page: ",
                                           implode(", ", $inserted_usernames), $username_prefix);
-                            //~ }
-                
-                
+
                 } else {// $number > 0
                     $failureMsg = "specify a valid number of accounts";
                     $logAction = "specified an invalid number of accounts on page: ";

programming_language: PHP
filename: mng-del.php
change_type: ModificationType.MODIFY
diff: @@ -32,167 +32,199 @@
     $logAction = "";
     $logDebugSQL = "";
 
-    isset($_GET['username']) ? $username = $_GET['username'] : $username = "";
-    isset($_GET['attribute']) ? $attribute = $_GET['attribute'] : $attribute = "";
-    isset($_GET['tablename']) ? $tablename = $_GET['tablename'] : $tablename = "";
-    isset($_GET['delradacct']) ? $delradacct = $_GET['delradacct'] : $delradacct = "";
-    isset($_GET['clearSessionsUsers']) ? $clearSessionsUsers = $_GET['clearSessionsUsers'] : $clearSessionsUsers = "";
-
-    $logAction = "";
-    $logDebugSQL = "";
-
-    $showRemoveDiv = "block";
-
-    if ( (isset($_GET['username'])) && (!(isset($_GET['attribute']))) && (!(isset($_GET['tablename']))) ) {
-
-        $allUsernames = "";
-        $isSuccessful = 0;
-
-        /* since the foreach loop will report an error/notice of undefined variable $value because
-           it is possible that the $username is not an array, but rather a simple GET request
-           with just some value, in this case we check if it's not an array and convert it to one with
-           a NULL 2nd element
-        */
-        if (!is_array($username))
-            $username = array($username, NULL);
-
-        foreach ($username as $variable=>$value) {
-
-            if (trim($variable) != "") {
-
-                $username = $value;
-                $allUsernames .= $username . ", ";
-
-                include 'library/opendb.php';
-
-                // setting table-related parameters first
-                switch($configValues['FREERADIUS_VERSION']) {
-                    case '1' :
-                        $tableSetting['postauth']['user'] = 'user';
-                        $tableSetting['postauth']['date'] = 'date';
-                        break;
-                    case '2' :
-                        // down
-                    case '3' :
-                        // down
-                    default  :
-                        $tableSetting['postauth']['user'] = 'username';
-                        $tableSetting['postauth']['date'] = 'authdate';
-                        break;
+    $valid_tablenames = array(
+                                $configValues['CONFIG_DB_TBL_RADCHECK'],
+                                $configValues['CONFIG_DB_TBL_RADREPLY'],
+                                $configValues['CONFIG_DB_TBL_RADGROUPREPLY'],
+                                $configValues['CONFIG_DB_TBL_RADGROUPCHECK']
+                             );
+
+    include('library/opendb.php');
+
+    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+        if (array_key_exists('csrf_token', $_POST) && isset($_POST['csrf_token']) && dalo_check_csrf_token($_POST['csrf_token'])) {
+                
+            $id__attribute = (array_key_exists('attribute', $_POST) && !empty(trim($_POST['attribute'])) &&
+                              preg_match("/__/", trim($_POST['attribute'])) !== false) ? trim($_POST['attribute']) : "";
+            
+            $tablename = (!empty($id__attribute) && array_key_exists('tablename', $_POST) &&
+                          !empty(trim($_POST['tablename'])) && in_array(trim($_POST['tablename']), $valid_tablenames))
+                       ? trim($_POST['tablename']) : "";
+
+            $delradacct = (array_key_exists('delradacct', $_POST) && strtolower(trim($_POST['delradacct'])) == 'yes');
+
+
+            // validate values
+            $usernames = array();
+            
+            if (array_key_exists('username', $_POST) && !empty($_POST['username'])) {
+                
+                $tmp = (!is_array($_POST['username'])) ? array($_POST['username']) : $_POST['username'];
+                foreach ($tmp as $value) {
+                    
+                    $value = trim(str_replace("%", "", $value));
+                    
+                    if (!in_array($value, $usernames)) {
+                        $usernames[] = $value;
+                    }
                 }
-
-                // delete all attributes associated with a username
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADCHECK']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADREPLY']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_DALOUSERINFO']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_DALOUSERBILLINFO']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADUSERGROUP']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADPOSTAUTH']." WHERE ".
-                    $tableSetting['postauth']['user']."='".$dbSocket->escapeSimple($username)."'";
-                $res = $dbSocket->query($sql);
-                $logDebugSQL .= $sql . "\n";
-
-                if (strtolower($delradacct) == "yes") {
-                    $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADACCT']." WHERE Username='".$dbSocket->escapeSimple($username)."'";
-                    $res = $dbSocket->query($sql);
-                    $logDebugSQL .= $sql . "\n";
+                
+                if (count($usernames) > 0) {
+                
+                    if (!empty($id__attribute) && !empty($tablename)) {
+                        
+                        $sql = sprintf("SELECT COUNT(id) FROM %s WHERE username='%s'",
+                                       $configValues['CONFIG_DB_TBL_RADCHECK'], $dbSocket->escapeSimple($usernames[0]));
+                        $res = $dbSocket->query($sql);
+                        $logDebugSQL .= "$sql;\n";
+                        
+                        $check_attr_count = intval($res->fetchrow()[0]);
+                        
+                        $sql = sprintf("SELECT COUNT(id) FROM %s WHERE username='%s' AND attribute='Auth-Type' OR attribute LIKE '%%-Password'",
+                                       $configValues['CONFIG_DB_TBL_RADCHECK'], $dbSocket->escapeSimple($usernames[0]));
+                        $res = $dbSocket->query($sql);
+                        $logDebugSQL .= "$sql;\n";
+                        
+                        $check_auth_attr_count = intval($res->fetchrow()[0]);
+                        
+                        list($columnId, $attribute) = explode("__", $id__attribute);
+                        $attribute = trim($attribute);
+                        $columnId = intval(trim($columnId));
+                        
+                        if ($tablename == 'radcheck' && $check_attr_count == 1 ||
+                            (($attribute == 'Auth-Type' || preg_match("/-Password$/", $attribute) !== false) && $check_auth_attr_count == 1)) {
+                            // if operator wants to remove the last check attribute
+                            // or the last "password-like" check attribute
+                            // they should delete all user related info stored in the db
+                            
+                            $format = "Cannot delete the last check (password like?) attribute for the selected user (<strong>%s</strong>)";
+                            $failureMsg = sprintf($format,
+                                                  htmlspecialchars($usernames[0], ENT_QUOTES, 'UTF-8'));
+                            $logAction = sprintf("$format on page: ", $username[0]);
+                        } else {
+                        
+                            $sql = sprintf("DELETE FROM %s WHERE username='%s' AND attribute='%s' AND id=%s",
+                                           $dbSocket->escapeSimple($tablename), $dbSocket->escapeSimple($usernames[0]),
+                                           $dbSocket->escapeSimple($attribute), $dbSocket->escapeSimple($columnId));
+                            $res = $dbSocket->query($sql);
+                            $logDebugSQL .= "$sql;\n";
+                            
+                            $format = "Deleted attribute <strong>%s</strong> for user <strong>%s</strong>";
+                            $successMsg = sprintf($format, htmlspecialchars($attribute, ENT_QUOTES, 'UTF-8'),
+                                                           htmlspecialchars($usernames[0], ENT_QUOTES, 'UTF-8'));
+                            $logAction = sprintf("$format on page: ", $attribute, $usernames[0]);
+                        }
+                    } else {
+                        $dbusers = array();
+                        
+                        foreach ($usernames as $u) {
+                            if (!empty($dbSocket->escapeSimple($u))) {
+                                $dbusers[] = $dbSocket->escapeSimple($u);
+                            }
+                        }
+                        
+                        $dbusersLen = count($dbusers);
+                        if ($dbusersLen > 0) {
+                            // setting table-related parameters first                
+                            switch($configValues['FREERADIUS_VERSION']) {
+                                case '1' :
+                                    $tableSetting['postauth']['user'] = 'user';
+                                    $tableSetting['postauth']['date'] = 'date';
+                                    break;
+                                case '2' :
+                                    // down
+                                case '3' :
+                                    // down
+                                default  :
+                                    $tableSetting['postauth']['user'] = 'username';
+                                    $tableSetting['postauth']['date'] = 'authdate';
+                                    break;
+                            }
+                            
+                            $sql_format = "DELETE FROM %s WHERE %s IN ('" . implode("', '", $dbusers) . "')";
+                            
+                            $sql = sprintf($sql_format, $configValues['CONFIG_DB_TBL_RADPOSTAUTH'],
+                                                        $tableSetting['postauth']['user']);
+                            $res = $dbSocket->query($sql);
+                            $logDebugSQL .= "$sql;\n";
+                            
+                            $tables = array(
+                                                $configValues['CONFIG_DB_TBL_RADCHECK'],
+                                                $configValues['CONFIG_DB_TBL_RADREPLY'],
+                                                $configValues['CONFIG_DB_TBL_DALOUSERINFO'],
+                                                $configValues['CONFIG_DB_TBL_DALOUSERBILLINFO'],
+                                                $configValues['CONFIG_DB_TBL_RADUSERGROUP'],
+                                           );
+
+                            if ($delradacct) {
+                                $tables[] = $configValues['CONFIG_DB_TBL_RADACCT'];
+                            }
+                            
+                            foreach ($tables as $table) {
+                                $sql = sprintf($sql_format, $table, 'username');
+                                $res = $dbSocket->query($sql);
+                                $logDebugSQL .= "$sql;\n";
+                            }
+                            
+                            $format = "<strong>%d user(s)</strong> have been deleted";
+                            $successMsg = sprintf($format, $dbusersLen);
+                            $logAction = sprintf("$format on page: ", $dbusersLen);
+                            
+                        } else {
+                            $failureMsg = "You have provided an empty or invalid username list";
+                            $logAction = "Provided an empty or invalid username list (user(s) deletion) on page: ";
+                        }
+                    }
+                } else {
+                    $failureMsg = "You have provided an empty or invalid username list";
+                    $logAction = "Provided an empty or invalid username list (user(s) deletion) on page: ";
+                }
+            } else if (array_key_exists('clearSessionsUsers', $_POST) && !empty($_POST['clearSessionsUsers'])) {
+                
+                $username__starttimes = array();
+                
+                $tmp = (!is_array($_POST['clearSessionsUsers'])) ? array($_POST['clearSessionsUsers']) : $_POST['clearSessionsUsers'];
+                foreach ($tmp as $value) {
+                    
+                    $value = trim(str_replace("%", "", $value));
+                    
+                    if (!in_array($value, $username__starttimes)) {
+                        $username__starttimes[] = $value;
+                    }
+                }
+                
+                
+                $userstimesLen = count($username__starttimes);
+                if ($userstimesLen > 0) {
+                    
+                    foreach ($username__starttimes as $username__starttime) {
+                        list($username, $starttime) = explode('||', $username__starttime);
+                        $sql = sprintf("DELETE FROM %s
+                                         WHERE username='%s' AND AcctStartTime='%s'
+                                           AND (AcctStopTime='0000-00-00 00:00:00' OR AcctStopTime IS NULL)",
+                                       $configValues['CONFIG_DB_TBL_RADACCT'], $dbSocket->escapeSimple($username),
+                                       $dbSocket->escapeSimple($starttime));
+                        $res = $dbSocket->query($sql);
+                        $logDebugSQL .= "$sql;\n";
+                    }
+                    
+                    $format = "<strong>%d user' session(s)</strong> have been cleaned";
+                    $successMsg = sprintf($format, $userstimesLen);
+                    $logAction = sprintf("$format on page: ", $userstimesLen);
+                    
+                } else {
+                    $failureMsg = "You have provided an empty or invalid username list";
+                    $logAction = "Provided an empty or invalid username list (session cleaning) on page: ";
                 }
-
-                $successMsg = "Deleted user(s): <b> $allUsernames </b>";
-                $logAction .= "Successfully deleted user(s) [$allUsernames] on page: ";
-
-                include 'library/closedb.php';
-
-            }  else {
-                $failureMsg = "no user was entered, please specify a username to remove from database";
-                $logAction .= "Failed deleting user(s) [$allUsernames] on page: ";
             }
-
-
-        $showRemoveDiv = "none";
-
-        } //foreach
-
-
-    } else     if ( (isset($_GET['username'])) && (isset($_GET['attribute'])) && (isset($_GET['tablename'])) ) {
-
-        /* this section of the deletion process only deletes the username record with the specified attribute
-         * variable from $tablename, this is in order to support just removing a single attribute for the user
-         */
-
-        include 'library/opendb.php';
-
-        if (isset($attribute)) {
-            if (preg_match('/__/', $attribute))
-                list($columnId, $attribute) = explode("__", $attribute);
-            else
-                $attribute = $attribute;
+        } else {
+            $failureMsg = sprintf("CSRF token error");
+            $logAction .= sprintf("CSRF token error on page: ");
         }
-
-        $sql = "DELETE FROM ".$dbSocket->escapeSimple($tablename)." WHERE Username='".$dbSocket->escapeSimple($username)."' ".
-                " AND Attribute='".$dbSocket->escapeSimple($attribute)."' AND id=".$dbSocket->escapeSimple($columnId);
-        $res = $dbSocket->query($sql);
-        $logDebugSQL .= $sql . "\n";
-
-        $successMsg = "Deleted attribute: <b> $attribute </b> for user(s): <b> $username </b> from database";
-        $logAction .= "Successfully deleted attribute [$attribute] for user [$username] on page: ";
-
-        include 'library/closedb.php';
-
-        $showRemoveDiv = "none";
-
-    } else if ( (isset($clearSessionsUsers)) && ($clearSessionsUsers != "") ) {
-
-        /* this is used to remove stale user sessions from the accounting table
-        */
-        $allUsernames = "";
-
-        if (!is_array($clearSessionsUsers))
-            $clearSessionsUsers = array($clearSessionsUsers, NULL);
-
-            foreach ($clearSessionsUsers as $variable=>$value) {
-
-                if (trim($value) != "") {
-
-                    list($userSessions,$acctStartTime) = preg_split('/\\|\\|/', $value);
-
-                    $allUsernames .= $userSessions . ", ";
-
-                    include 'library/opendb.php';
-
-                    $sql = "DELETE FROM ".$configValues['CONFIG_DB_TBL_RADACCT'].
-                        " WHERE Username='$userSessions' AND AcctStartTime='$acctStartTime' ".
-                        " AND (AcctStopTime='0000-00-00 00:00:00' OR AcctStopTime IS NULL)";
-                    $res = $dbSocket->query($sql);
-                    $logDebugSQL .= $sql . "\n";
-
-                    $successMsg = "Deleted stale accounting sessions for user: <b> $allUsernames </b> from database";
-                    $logAction .= "Successfully deleted stale accounting sessions for user [$allUsernames] on page: ";
-
-                    include 'library/closedb.php';
-            } // if trim
-
-        } // foreach
-
-        $showRemoveDiv = "none";
-
     }
 
+    include('library/closedb.php');
+
     include_once("lang/main.php");
     include("library/layout.php");
 
@@ -213,50 +245,75 @@
 
     include_once('include/management/actionMessages.php');
 
+    include('library/opendb.php');
+    
+    $sql = sprintf("SELECT DISTINCT(username) FROM %s", $configValues['CONFIG_DB_TBL_RADCHECK']);
+    $res = $dbSocket->query($sql);
+    $logDebugSQL .= "$sql;\n";
+    
+    $options = array();
+    while ($row = $res->fetchrow()) {
+        $options[] = $row[0];
+    }
+    
+    include('library/closedb.php');
+
+    $input_descriptors1 = array();
+
+    $input_descriptors1[] = array(
+                                'name' => 'username[]',
+                                'id' => 'username',
+                                'type' => 'select',
+                                'caption' => t('all','Username'),
+                                'options' => $options,
+                                'multiple' => true,
+                                'size' => 5
+                             );
+
+    $input_descriptors1[] = array(
+                                'name' => 'delradacct',
+                                'type' => 'select',
+                                'caption' => t('all','RemoveRadacctRecords'),
+                                'options' => array("", "yes", "no"),
+                             );
+
+    $input_descriptors1[] = array(
+                                    "name" => "csrf_token",
+                                    "type" => "hidden",
+                                    "value" => dalo_csrf_token(),
+                                 );
+
+    $input_descriptors1[] = array(
+                                    'type' => 'submit',
+                                    'name' => 'submit',
+                                    'value' => t('buttons','apply')
+                                 );
 ?>
 
-    <div id="removeDiv" style="display:<?php echo $showRemoveDiv ?>;visibility:visible" >
-    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
-
+<form method="POST">
     <fieldset>
+        <h302><?= t('title','AccountRemoval') ?></h302>
+        <ul style="margin: 10px auto">
+<?php
+        foreach ($input_descriptors1 as $input_descriptor) {
+            print_form_component($input_descriptor);
+        }
+?>
 
-        <h302> <?php echo t('title','AccountRemoval') ?> </h302>
-        <br/>
-
-        <label for='username' class='form'><?php echo t('all','Username')?></label>
-        <input name='username[]' type='text' id='username' value='<?php echo $username ?>' tabindex=100 />
-        <br />
-
-        <label for='delradacct' class='form'><?php echo t('all','RemoveRadacctRecords')?></label>
-        <select class='form' tabindex=102 name='delradacct' tabindex=101>
-            <option value='no'>no</option>
-            <option value='yes'>yes</option>
-        </select>
-        <br />
-
-        <br/><br/>
-        <hr><br/>
-        <input type="submit" name="submit" value="<?php echo t('buttons','apply') ?>" tabindex=1000
-            class='button' />
-
+        </ul>
     </fieldset>
+</form>
+<?php
 
-    </form>
-    </div>
+    if (array_key_exists('PREV_LIST_PAGE', $_SESSION) && !empty(trim($_SESSION['PREV_LIST_PAGE']))) {
+        echo '<div style="float: right; text-align: right; margin: 0; font-size: small">';
+        printf('<a href="%s" title="Back to Previous Page">Back to Previous Page</a>', trim($_SESSION['PREV_LIST_PAGE']));
+        echo '</div>';
+        
+        unset($_SESSION['PREV_LIST_PAGE']);
+    }
 
-<?php
     include('include/config/logging.php');
-    
-    include_once("include/management/autocomplete.php");
-    
-    if ($autoComplete) {
-         $inline_extra_js = "
-autoComEdit = new DHTMLSuite.autoComplete();
-autoComEdit.add('username','include/management/dynamicAutocomplete.php','_small','getAjaxAutocompleteUsernames');";
-    } else {
-        $inline_extra_js = "";
-    }
-    
-    print_footer_and_html_epilogue($inline_extra_js);
+    print_footer_and_html_epilogue();
 ?>
 

programming_language: PHP
filename: mng-edit.php
change_type: ModificationType.MODIFY
diff: @@ -473,6 +473,12 @@ window.onload = function(){
     if (!empty($username)) {
         $input_descriptors1 = array();
         
+        $input_descriptors1[] = array(
+                                        "type" => "hidden",
+                                        "value" => dalo_csrf_token(),
+                                        "name" => "csrf_token"
+                                     );
+        
         $input_descriptors1[] = array(
                                         "type" => "hidden",
                                         "value" => $username_enc,
@@ -610,10 +616,10 @@ window.onload = function(){
             $id__attribute = sprintf('%s__%s', $row[5], $row[0]);
             $name = sprintf('editValues%s[]', $editCounter);
             $type = (preg_match("/-Password$/", $row[0])) ? $hiddenPassword : "text";
-
+            $onclick = sprintf("document.getElementById('form-%d-radcheck').submit()", $editCounter);
+            
             echo '<li>';
-            printf('<a class="tablenovisit" href="mng-del.php?username=%s&attribute=%s&tablename=radcheck">',
-                   urlencode($username_enc), urlencode($id__attribute));
+            printf('<a class="tablenovisit" href="#" onclick="%s">', $onclick);
             echo '<img src="images/icons/delete.png" border="0" alt="Remove"></a>';
             
             printf('<label for="attribute" class="attributes">%s</label>', $row[0]);
@@ -630,7 +636,7 @@ window.onload = function(){
 
 
             if (!empty($row[3]) || !empty($row[4])) {
-                $divId = sprintf("%s-Tooltip-%d-check", $row[0], $editCounter);
+                $divId = sprintf("%s-Tooltip-%d-radcheck", $row[0], $editCounter);
                 $onclick = sprintf("toggleShowDiv('%s')", $divId);
                 printf('<img src="images/icons/comment.png" alt="Tip" border="0" onClick="%s">', $onclick);
                 printf('<div id="%s" style="display:none;visibility:visible" class="ToolTip2">', $divId);
@@ -707,10 +713,10 @@ window.onload = function(){
             $id__attribute = sprintf('%s__%s', $row[5], $row[0]);
             $name = sprintf('editValues%s[]', $editCounter);
             $type = (preg_match("/-Password$/", $row[0])) ? $hiddenPassword : "text";
-    
+            $onclick = sprintf("document.getElementById('form-%d-radreply').submit()", $editCounter);
+            
             echo '<li>';
-            printf('<a class="tablenovisit" href="mng-del.php?username=%s&attribute=%s&tablename=radreply">',
-                   urlencode($username_enc), urlencode($id__attribute));
+            printf('<a class="tablenovisit" href="#" onclick="%s">', $onclick);
             echo '<img src="images/icons/delete.png" border="0" alt="Remove"></a>';
 
             printf('<label for="attribute" class="attributes">%s</label>', $row[0]);
@@ -726,7 +732,7 @@ window.onload = function(){
             printf('<input type="hidden" name="%s" value="radreply">', $name);
 
             if (!empty($row[3]) || !empty($row[4])) {
-                $divId = sprintf("%s-Tooltip-%d-reply", $row[0], $editCounter);
+                $divId = sprintf("%s-Tooltip-%d-radreply", $row[0], $editCounter);
                 $onclick = sprintf("toggleShowDiv('%s')", $divId);
                 printf('<img src="images/icons/comment.png" alt="Tip" border="0" onClick="%s">', $onclick);
                 printf('<div id="%s" style="display:none;visibility:visible" class="ToolTip2">', $divId);
@@ -843,6 +849,44 @@ window.onload = function(){
 
 </form>
 
+<?php
+    include('library/opendb.php');
+
+    $tables = array(
+                        'radcheck' => $configValues['CONFIG_DB_TBL_RADCHECK'],
+                        'radreply' => $configValues['CONFIG_DB_TBL_RADREPLY']
+                   );
+
+    $csrf_token = dalo_csrf_token();
+
+    foreach ($tables as $table_value => $table) {
+
+        $sql = sprintf("SELECT id, attribute, value FROM %s WHERE username='%s'",
+                       $table, $dbSocket->escapeSimple($username));
+        $res = $dbSocket->query($sql);
+        $logDebugSQL .= "$sql;\n";
+
+        if ($res->numRows() > 0) {
+            $counter = 0;
+            while ($row = $res->fetchrow()) {
+                list($id, $attribute, $value) = $row;
+                
+                $formId = sprintf("form-%d-%s", $counter, $table_value);
+                $id__attribute = sprintf("%d__%s", intval($id), htmlspecialchars($attribute, ENT_QUOTES, 'UTF-8'));
+                
+                printf('<form id="%s" style="display: none" method="POST" action="mng-del.php">', $formId);
+                printf('<input type="hidden" name="username" value="%s">', $username_enc);
+                printf('<input type="hidden" name="attribute" value="%s">', $id__attribute);
+                printf('<input type="hidden" name="csrf_token" value="%s">', $csrf_token);
+                printf('<input type="hidden" name="tablename" value="%s">', $table_value);
+                echo '</form>';
+                
+                $counter++;
+            }
+        }
+    }
+    include('library/closedb.php');
+?>
     <div id="OtherInfo-tab" class="tabcontent" title="Other Info">
 <?php
         include_once('include/management/userReports.php');

programming_language: PHP
filename: mng-list-all.php
change_type: ModificationType.MODIFY
diff: @@ -195,7 +195,7 @@
         $action = "mng-del.php";
 ?>
 
-<form name="listall" method="GET" action="<?= $action ?>">
+<form name="listall" method="POST" action="<?= $action ?>">
 
     <table border="0" class="table1">
         <thead>
@@ -289,6 +289,9 @@
 ?>
 
     </table>
+
+    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">
+    
 </form>
 
 <?php

programming_language: PHP
filename: mng-rad-profiles-list.php
change_type: ModificationType.MODIFY
diff: @@ -184,11 +184,11 @@
     include('include/config/logging.php');
     
     $inline_extra_js = "
-    var tooltipObj = new DHTMLgoodies_formTooltip();
-    tooltipObj.setTooltipPosition('right');
-    tooltipObj.setPageBgColor('#EEEEEE');
-    tooltipObj.setTooltipCornerSize(15);
-    tooltipObj.initFormFieldTooltip();";
+var tooltipObj = new DHTMLgoodies_formTooltip();
+tooltipObj.setTooltipPosition('right');
+tooltipObj.setPageBgColor('#EEEEEE');
+tooltipObj.setTooltipCornerSize(15);
+tooltipObj.initFormFieldTooltip();";
     
     print_footer_and_html_epilogue($inline_extra_js);
 ?>

programming_language: PHP
filename: mng-search.php
change_type: ModificationType.MODIFY
diff: @@ -50,6 +50,16 @@
                   ? htmlspecialchars($username, ENT_QUOTES, 'UTF-8')
                   : "";
 
+    // init logging variables
+    $log = "visited page: ";
+    $logQuery = "performed query for ";
+    if (!empty($username)) {
+         $logQuery .= "username(s) starting with [$username] ";
+    } else {
+        $logQuery .= "all usernames ";
+    }
+    $logQuery .= "on page: ";
+
     //feed the sidebar variables
     $search_username = $username_enc;
 
@@ -142,9 +152,13 @@
         // the partial query is built starting from user input
         // and for being passed to setupNumbering and setupLinks functions
         $partial_query_string = (!empty($username_enc) ? "&usernameOnline=" . urlencode($username_enc) : "");
+        
+        // this can be passed as form attribute and 
+        // printTableFormControls function parameter
+        $action = "mng-del.php";
 ?>
         
-<form name="searchusers" method="GET" action="mng-del.php">
+<form name="searchusers" method="POST" action="<?= $action ?>">
     <table border="0" class="table1">
         <thead>
             <tr style="background-color: white">
@@ -164,7 +178,7 @@
             <tr>
                 <th style="text-align: left" colspan="<?= $colspan ?>">
 <?php
-        printTableFormControls('username[]', 'mng-del.php', 'searchusers');
+        printTableFormControls('username[]', $action, 'searchusers');
 ?>
                 </th>
             </tr>
@@ -235,6 +249,9 @@
 ?>
 
     </table>
+    
+    <input type="hidden" name="csrf_token" value="<?= dalo_csrf_token() ?>">
+    
 </form>
 
 <?php
@@ -244,38 +261,15 @@
     }
     
     include('library/closedb.php');
-?>
-
-        </div><!-- #contentnorightbar -->
-
-        <div id="footer">
-
-<?php
-    $log = "visited page: ";
-    $logQuery = "performed query for ";
-    if (!empty($username)) {
-         $logQuery .= "username(s) starting with [$username] ";
-    } else {
-        $logQuery .= "all usernames ";
-    }
-    $logQuery .= "on page: ";
 
     include('include/config/logging.php');
-    include('page-footer.php');
-?>
-
-
-        </div><!-- #footer -->
-    </div>
-</div>
-
-<script>
-    var tooltipObj = new DHTMLgoodies_formTooltip();
-    tooltipObj.setTooltipPosition('right');
-    tooltipObj.setPageBgColor('#EEEEEE');
-    tooltipObj.setTooltipCornerSize(15);
-    tooltipObj.initFormFieldTooltip();
-</script>
+    
+    $inline_extra_js = "
+var tooltipObj = new DHTMLgoodies_formTooltip();
+tooltipObj.setTooltipPosition('right');
+tooltipObj.setPageBgColor('#EEEEEE');
+tooltipObj.setTooltipCornerSize(15);
+tooltipObj.initFormFieldTooltip()";
 
-</body>
-</html>
+    print_footer_and_html_epilogue($inline_extra_js);
+?>

programming_language: PHP
filename: common.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,92 @@
+<?php 
+/*
+ *********************************************************************************************************
+ * daloRADIUS - RADIUS Web Platform
+ * Copyright (C) 2007 - Liran Tal <liran@enginx.com> All Rights Reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *********************************************************************************************************
+ *
+ * Authors:    Filippo Lauria <filippo.lauria@iit.cnr.it>
+ *
+ *********************************************************************************************************
+ */
+
+// prevent this file to be directly accessed
+if (strpos($_SERVER['PHP_SELF'], '/notifications/common.php') !== false) {
+    header("Location: ../index.php");
+    exit;
+}
+
+//include the dompdf class
+require_once("dompdf/dompdf_config.inc.php");
+
+//include the Pear Mail classes for sending out emails
+@require_once('Mail.php');
+@require_once('Mail/mime.php');
+
+/**
+ * createPDF()
+ * creates a PDF document for a given html file
+ * 
+ * @param         string        the html file to convert to pdf
+ * 
+ * @return        string        returns the pdf in binary/string stream
+ */
+function createPDF($html, $base_path) {
+    // this fix has been set in place according to this thread:
+    // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
+    $html = str_replace("\n", "", $html);
+    
+    // instansiate the pdf document
+    $dompdf = new DOMPDF();
+    $dompdf->set_base_path($base_path);
+    $dompdf->load_html($html);
+    $dompdf->render();
+    return $dompdf->output();
+}
+
+
+/**
+ * send_notification_via_email()
+ * creates an email message with the pdf and sends it
+ * 
+ * @param        string        pdf binary/string stream
+ * @param        array         smtp server information
+ * @param        string        destination email address
+ * @param        string        from email address of the sender identity
+ * @param        string        email subject
+ * @param        string        email body
+ * @param        string        pdf attachment name
+ * 
+ */
+function send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName) {
+    if (filter_var($sendTo, FILTER_VALIDATE_EMAIL) === false) {
+        return false;
+    }
+    
+    $headers = array(
+                       "From"     => $from, 
+                       "Subject"  => $subject,
+                       "Reply-To" => $from
+                    );
+
+    $mime = new Mail_mime();
+    $mime->setTXTBody($body); 
+    $mime->addAttachment($pdfDocument, "application/pdf", $attachmentName, false, 'base64');
+    $body = $mime->get();
+    $headers = $mime->headers($headers);
+    $mail =& Mail::factory("smtp", $smtpInfo);
+    $mail->send($sendTo, $headers, $body);
+
+    return true;
+}
+?>

programming_language: PHP
filename: dompdf_config.inc.php
change_type: ModificationType.MODIFY
diff: @@ -331,7 +331,7 @@ require_once(DOMPDF_LIB_DIR . "/html5lib/Parser.php");
  */
 if (DOMPDF_ENABLE_AUTOLOAD) {
   require_once(DOMPDF_INC_DIR . "/autoload.inc.php");
-  require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/Font.php");
+  require_once(DOMPDF_LIB_DIR . "/php-font-lib/Font.php");
 }
 
 /**

programming_language: PHP
filename: AdobeFontMetrics.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,217 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+use FontLib\Table\Type\name;
+use FontLib\TrueType\File;
+
+/**
+ * Adobe Font Metrics file creation utility class.
+ *
+ * @package php-font-lib
+ */
+class AdobeFontMetrics {
+  private $f;
+
+  /**
+   * @var File
+   */
+  private $font;
+
+  function __construct(File $font) {
+    $this->font = $font;
+  }
+
+  function write($file, $encoding = null) {
+    $map_data = array();
+
+    if ($encoding) {
+      $encoding = preg_replace("/[^a-z0-9-_]/", "", $encoding);
+      $map_file = dirname(__FILE__) . "/../maps/$encoding.map";
+      if (!file_exists($map_file)) {
+        throw new \Exception("Unknown encoding ($encoding)");
+      }
+
+      $map      = new EncodingMap($map_file);
+      $map_data = $map->parse();
+    }
+
+    $this->f = fopen($file, "w+");
+
+    $font = $this->font;
+
+    $this->startSection("FontMetrics", 4.1);
+    $this->addPair("Notice", "Converted by PHP-font-lib");
+    $this->addPair("Comment", "https://github.com/PhenX/php-font-lib");
+
+    $encoding_scheme = ($encoding ? $encoding : "FontSpecific");
+    $this->addPair("EncodingScheme", $encoding_scheme);
+
+    $records = $font->getData("name", "records");
+    foreach ($records as $id => $record) {
+      if (!isset(name::$nameIdCodes[$id]) || preg_match("/[\r\n]/", $record->string)) {
+        continue;
+      }
+
+      $this->addPair(name::$nameIdCodes[$id], $record->string);
+    }
+
+    $os2 = $font->getData("OS/2");
+    $this->addPair("Weight", ($os2["usWeightClass"] > 400 ? "Bold" : "Medium"));
+
+    $post = $font->getData("post");
+    $this->addPair("ItalicAngle", $post["italicAngle"]);
+    $this->addPair("IsFixedPitch", ($post["isFixedPitch"] ? "true" : "false"));
+    $this->addPair("UnderlineThickness", $font->normalizeFUnit($post["underlineThickness"]));
+    $this->addPair("UnderlinePosition", $font->normalizeFUnit($post["underlinePosition"]));
+
+    $hhea = $font->getData("hhea");
+
+    if (isset($hhea["ascent"])) {
+      $this->addPair("FontHeightOffset", $font->normalizeFUnit($hhea["lineGap"]));
+      $this->addPair("Ascender", $font->normalizeFUnit($hhea["ascent"]));
+      $this->addPair("Descender", $font->normalizeFUnit($hhea["descent"]));
+    }
+    else {
+      $this->addPair("FontHeightOffset", $font->normalizeFUnit($os2["typoLineGap"]));
+      $this->addPair("Ascender", $font->normalizeFUnit($os2["typoAscender"]));
+      $this->addPair("Descender", -abs($font->normalizeFUnit($os2["typoDescender"])));
+    }
+
+    $head = $font->getData("head");
+    $this->addArray("FontBBox", array(
+      $font->normalizeFUnit($head["xMin"]),
+      $font->normalizeFUnit($head["yMin"]),
+      $font->normalizeFUnit($head["xMax"]),
+      $font->normalizeFUnit($head["yMax"]),
+    ));
+
+    $glyphIndexArray = $font->getUnicodeCharMap();
+
+    if ($glyphIndexArray) {
+      $hmtx  = $font->getData("hmtx");
+      $names = $font->getData("post", "names");
+
+      $this->startSection("CharMetrics", count($hmtx));
+
+      if ($encoding) {
+        foreach ($map_data as $code => $value) {
+          list($c, $name) = $value;
+
+          if (!isset($glyphIndexArray[$c])) {
+            continue;
+          }
+
+          $g = $glyphIndexArray[$c];
+
+          if (!isset($hmtx[$g])) {
+            $hmtx[$g] = $hmtx[0];
+          }
+
+          $this->addMetric(array(
+            "C"  => ($code > 255 ? -1 : $code),
+            "WX" => $font->normalizeFUnit($hmtx[$g][0]),
+            "N"  => $name,
+          ));
+        }
+      }
+      else {
+        foreach ($glyphIndexArray as $c => $g) {
+          if (!isset($hmtx[$g])) {
+            $hmtx[$g] = $hmtx[0];
+          }
+
+          $this->addMetric(array(
+            "U"  => $c,
+            "WX" => $font->normalizeFUnit($hmtx[$g][0]),
+            "N"  => (isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $c)),
+            "G"  => $g,
+          ));
+        }
+      }
+
+      $this->endSection("CharMetrics");
+
+      $kern = $font->getData("kern", "subtable");
+      $tree = is_array($kern) ? $kern["tree"] : null;
+
+      if (!$encoding && is_array($tree)) {
+        $this->startSection("KernData");
+        $this->startSection("KernPairs", count($tree, COUNT_RECURSIVE) - count($tree));
+
+        foreach ($tree as $left => $values) {
+          if (!is_array($values)) {
+            continue;
+          }
+          if (!isset($glyphIndexArray[$left])) {
+            continue;
+          }
+
+          $left_gid = $glyphIndexArray[$left];
+
+          if (!isset($names[$left_gid])) {
+            continue;
+          }
+
+          $left_name = $names[$left_gid];
+
+          $this->addLine("");
+
+          foreach ($values as $right => $value) {
+            if (!isset($glyphIndexArray[$right])) {
+              continue;
+            }
+
+            $right_gid = $glyphIndexArray[$right];
+
+            if (!isset($names[$right_gid])) {
+              continue;
+            }
+
+            $right_name = $names[$right_gid];
+            $this->addPair("KPX", "$left_name $right_name $value");
+          }
+        }
+
+        $this->endSection("KernPairs");
+        $this->endSection("KernData");
+      }
+    }
+
+    $this->endSection("FontMetrics");
+  }
+
+  function addLine($line) {
+    fwrite($this->f, "$line\n");
+  }
+
+  function addPair($key, $value) {
+    $this->addLine("$key $value");
+  }
+
+  function addArray($key, $array) {
+    $this->addLine("$key " . implode(" ", $array));
+  }
+
+  function addMetric($data) {
+    $array = array();
+    foreach ($data as $key => $value) {
+      $array[] = "$key $value";
+    }
+    $this->addLine(implode(" ; ", $array));
+  }
+
+  function startSection($name, $value = "") {
+    $this->addLine("Start$name $value");
+  }
+
+  function endSection($name) {
+    $this->addLine("End$name");
+  }
+}

programming_language: PHP
filename: Autoloader.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,43 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Mnager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+/**
+ * Autoloads FontLib classes
+ *
+ * @package php-font-lib
+ */
+class Autoloader {
+  const PREFIX = 'FontLib';
+
+  /**
+   * Register the autoloader
+   */
+  public static function register() {
+    spl_autoload_register(array(new self, 'autoload'));
+  }
+
+  /**
+   * Autoloader
+   *
+   * @param string
+   */
+  public static function autoload($class) {
+    $prefixLength = strlen(self::PREFIX);
+    if (0 === strncmp(self::PREFIX, $class, $prefixLength)) {
+      $file = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, $prefixLength));
+      $file = realpath(__DIR__ . (empty($file) ? '' : DIRECTORY_SEPARATOR) . $file . '.php');
+      if (file_exists($file)) {
+        require_once $file;
+      }
+    }
+  }
+}
+
+Autoloader::register();
\ No newline at end of file

programming_language: PHP
filename: BinaryStream.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,449 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+/**
+ * Generic font file binary stream.
+ *
+ * @package php-font-lib
+ */
+class BinaryStream {
+  /**
+   * @var resource The file pointer
+   */
+  protected $f;
+
+  const uint8        = 1;
+  const  int8        = 2;
+  const uint16       = 3;
+  const  int16       = 4;
+  const uint32       = 5;
+  const  int32       = 6;
+  const shortFrac    = 7;
+  const Fixed        = 8;
+  const  FWord       = 9;
+  const uFWord       = 10;
+  const F2Dot14      = 11;
+  const longDateTime = 12;
+  const char         = 13;
+
+  const modeRead      = "rb";
+  const modeWrite     = "wb";
+  const modeReadWrite = "rb+";
+
+  static function backtrace() {
+    var_dump(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
+  }
+
+  /**
+   * Open a font file in read mode
+   *
+   * @param string $filename The file name of the font to open
+   *
+   * @return bool
+   */
+  public function load($filename) {
+    return $this->open($filename, self::modeRead);
+  }
+
+  /**
+   * Open a font file in a chosen mode
+   *
+   * @param string $filename The file name of the font to open
+   * @param string $mode     The opening mode
+   *
+   * @throws \Exception
+   * @return bool
+   */
+  public function open($filename, $mode = self::modeRead) {
+    if (!in_array($mode, array(self::modeRead, self::modeWrite, self::modeReadWrite))) {
+      throw new \Exception("Unknown file open mode");
+    }
+
+    $this->f = fopen($filename, $mode);
+
+    return $this->f != false;
+  }
+
+  /**
+   * Close the internal file pointer
+   */
+  public function close() {
+    return fclose($this->f) != false;
+  }
+
+  /**
+   * Change the internal file pointer
+   *
+   * @param resource $fp
+   *
+   * @throws \Exception
+   */
+  public function setFile($fp) {
+    if (!is_resource($fp)) {
+      throw new \Exception('$fp is not a valid resource');
+    }
+
+    $this->f = $fp;
+  }
+
+  /**
+   * Create a temporary file in write mode
+   *
+   * @param bool $allow_memory Allow in-memory files
+   *
+   * @return resource the temporary file pointer resource
+   */
+  public static function getTempFile($allow_memory = true) {
+    $f = null;
+
+    if ($allow_memory) {
+      $f = fopen("php://temp", "rb+");
+    }
+    else {
+      $f = fopen(tempnam(sys_get_temp_dir(), "fnt"), "rb+");
+    }
+
+    return $f;
+  }
+
+  /**
+   * Move the internal file pinter to $offset bytes
+   *
+   * @param int $offset
+   *
+   * @return bool True if the $offset position exists in the file
+   */
+  public function seek($offset) {
+    return fseek($this->f, $offset, SEEK_SET) == 0;
+  }
+
+  /**
+   * Gives the current position in the file
+   *
+   * @return int The current position
+   */
+  public function pos() {
+    return ftell($this->f);
+  }
+
+  public function skip($n) {
+    fseek($this->f, $n, SEEK_CUR);
+  }
+
+  /**
+   * @param int $n The number of bytes to read
+   *
+   * @return string
+   */
+  public function read($n) {
+    if ($n < 1) {
+      return "";
+    }
+
+    return (string) fread($this->f, $n);
+  }
+
+  public function write($data, $length = null) {
+    if ($data === null || $data === "" || $data === false) {
+      return 0;
+    }
+
+    return fwrite($this->f, $data, $length);
+  }
+
+  public function readUInt8() {
+    return ord($this->read(1));
+  }
+
+  public function readUInt8Many($count) {
+    return array_values(unpack("C*", $this->read($count)));
+  }
+
+  public function writeUInt8($data) {
+    return $this->write(chr($data), 1);
+  }
+
+  public function readInt8() {
+    $v = $this->readUInt8();
+
+    if ($v >= 0x80) {
+      $v -= 0x100;
+    }
+
+    return $v;
+  }
+
+  public function readInt8Many($count) {
+    return array_values(unpack("c*", $this->read($count)));
+  }
+
+  public function writeInt8($data) {
+    if ($data < 0) {
+      $data += 0x100;
+    }
+
+    return $this->writeUInt8($data);
+  }
+
+  public function readUInt16() {
+    $a = unpack("nn", $this->read(2));
+
+    return $a["n"];
+  }
+
+  public function readUInt16Many($count) {
+    return array_values(unpack("n*", $this->read($count * 2)));
+  }
+
+  public function readUFWord() {
+    return $this->readUInt16();
+  }
+
+  public function writeUInt16($data) {
+    return $this->write(pack("n", $data), 2);
+  }
+
+  public function writeUFWord($data) {
+    return $this->writeUInt16($data);
+  }
+
+  public function readInt16() {
+    $a = unpack("nn", $this->read(2));
+    $v = $a["n"];
+
+    if ($v >= 0x8000) {
+      $v -= 0x10000;
+    }
+
+    return $v;
+  }
+
+  public function readInt16Many($count) {
+    $vals = array_values(unpack("n*", $this->read($count * 2)));
+    foreach ($vals as &$v) {
+      if ($v >= 0x8000) {
+        $v -= 0x10000;
+      }
+    }
+
+    return $vals;
+  }
+
+  public function readFWord() {
+    return $this->readInt16();
+  }
+
+  public function writeInt16($data) {
+    if ($data < 0) {
+      $data += 0x10000;
+    }
+
+    return $this->writeUInt16($data);
+  }
+
+  public function writeFWord($data) {
+    return $this->writeInt16($data);
+  }
+
+  public function readUInt32() {
+    $a = unpack("NN", $this->read(4));
+
+    return $a["N"];
+  }
+
+  public function writeUInt32($data) {
+    return $this->write(pack("N", $data), 4);
+  }
+
+  public function readFixed() {
+    $d  = $this->readInt16();
+    $d2 = $this->readUInt16();
+
+    return round($d + $d2 / 0x10000, 4);
+  }
+
+  public function writeFixed($data) {
+    $left  = floor($data);
+    $right = ($data - $left) * 0x10000;
+
+    return $this->writeInt16($left) + $this->writeUInt16($right);
+  }
+
+  public function readLongDateTime() {
+    $this->readUInt32(); // ignored
+    $date = $this->readUInt32() - 2082844800;
+    
+    # PHP_INT_MIN isn't defined in PHP < 7.0
+    $php_int_min = defined("PHP_INT_MIN") ? PHP_INT_MIN : ~PHP_INT_MAX;
+
+    if (is_string($date) || $date > PHP_INT_MAX || $date < $php_int_min) {
+      $date = 0;
+    }
+
+    return date("Y-m-d H:i:s", $date);
+  }
+
+  public function writeLongDateTime($data) {
+    $date = strtotime($data);
+    $date += 2082844800;
+
+    return $this->writeUInt32(0) + $this->writeUInt32($date);
+  }
+
+  public function unpack($def) {
+    $d = array();
+    foreach ($def as $name => $type) {
+      $d[$name] = $this->r($type);
+    }
+
+    return $d;
+  }
+
+  public function pack($def, $data) {
+    $bytes = 0;
+    foreach ($def as $name => $type) {
+      $bytes += $this->w($type, $data[$name]);
+    }
+
+    return $bytes;
+  }
+
+  /**
+   * Read a data of type $type in the file from the current position
+   *
+   * @param mixed $type The data type to read
+   *
+   * @return mixed The data that was read
+   */
+  public function r($type) {
+    switch ($type) {
+      case self::uint8:
+        return $this->readUInt8();
+      case self::int8:
+        return $this->readInt8();
+      case self::uint16:
+        return $this->readUInt16();
+      case self::int16:
+        return $this->readInt16();
+      case self::uint32:
+        return $this->readUInt32();
+      case self::int32:
+        return $this->readUInt32();
+      case self::shortFrac:
+        return $this->readFixed();
+      case self::Fixed:
+        return $this->readFixed();
+      case self::FWord:
+        return $this->readInt16();
+      case self::uFWord:
+        return $this->readUInt16();
+      case self::F2Dot14:
+        return $this->readInt16();
+      case self::longDateTime:
+        return $this->readLongDateTime();
+      case self::char:
+        return $this->read(1);
+      default:
+        if (is_array($type)) {
+          if ($type[0] == self::char) {
+            return $this->read($type[1]);
+          }
+          if ($type[0] == self::uint16) {
+            return $this->readUInt16Many($type[1]);
+          }
+          if ($type[0] == self::int16) {
+            return $this->readInt16Many($type[1]);
+          }
+          if ($type[0] == self::uint8) {
+            return $this->readUInt8Many($type[1]);
+          }
+          if ($type[0] == self::int8) {
+            return $this->readInt8Many($type[1]);
+          }
+
+          $ret = array();
+          for ($i = 0; $i < $type[1]; $i++) {
+            $ret[] = $this->r($type[0]);
+          }
+
+          return $ret;
+        }
+
+        return null;
+    }
+  }
+
+  /**
+   * Write $data of type $type in the file from the current position
+   *
+   * @param mixed $type The data type to write
+   * @param mixed $data The data to write
+   *
+   * @return int The number of bytes read
+   */
+  public function w($type, $data) {
+    switch ($type) {
+      case self::uint8:
+        return $this->writeUInt8($data);
+      case self::int8:
+        return $this->writeInt8($data);
+      case self::uint16:
+        return $this->writeUInt16($data);
+      case self::int16:
+        return $this->writeInt16($data);
+      case self::uint32:
+        return $this->writeUInt32($data);
+      case self::int32:
+        return $this->writeUInt32($data);
+      case self::shortFrac:
+        return $this->writeFixed($data);
+      case self::Fixed:
+        return $this->writeFixed($data);
+      case self::FWord:
+        return $this->writeInt16($data);
+      case self::uFWord:
+        return $this->writeUInt16($data);
+      case self::F2Dot14:
+        return $this->writeInt16($data);
+      case self::longDateTime:
+        return $this->writeLongDateTime($data);
+      case self::char:
+        return $this->write($data, 1);
+      default:
+        if (is_array($type)) {
+          if ($type[0] == self::char) {
+            return $this->write($data, $type[1]);
+          }
+
+          $ret = 0;
+          for ($i = 0; $i < $type[1]; $i++) {
+            if (isset($data[$i])) {
+              $ret += $this->w($type[0], $data[$i]);
+            }
+          }
+
+          return $ret;
+        }
+
+        return null;
+    }
+  }
+
+  /**
+   * Converts a Uint32 value to string
+   *
+   * @param int $uint32
+   *
+   * @return string The string
+   */
+  public function convertUInt32ToStr($uint32) {
+    return chr(($uint32 >> 24) & 0xFF) . chr(($uint32 >> 16) & 0xFF) . chr(($uint32 >> 8) & 0xFF) . chr($uint32 & 0xFF);
+  }
+}

programming_language: PHP
filename: File.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,159 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\EOT;
+
+/**
+ * EOT font file.
+ *
+ * @package php-font-lib
+ */
+class File extends \FontLib\TrueType\File {
+  const TTEMBED_SUBSET                   = 0x00000001;
+  const TTEMBED_TTCOMPRESSED             = 0x00000004;
+  const TTEMBED_FAILIFVARIATIONSIMULATED = 0x00000010;
+  const TTMBED_EMBEDEUDC                 = 0x00000020;
+  const TTEMBED_VALIDATIONTESTS          = 0x00000040; // Deprecated
+  const TTEMBED_WEBOBJECT      = 0x00000080;
+  const TTEMBED_XORENCRYPTDATA = 0x10000000;
+
+  /**
+   * @var Header
+   */
+  public $header;
+
+  function parseHeader() {
+    if (!empty($this->header)) {
+      return;
+    }
+
+    $this->header = new Header($this);
+    $this->header->parse();
+  }
+
+  function parse() {
+    $this->parseHeader();
+
+    $flags = $this->header->data["Flags"];
+
+    if ($flags & self::TTEMBED_TTCOMPRESSED) {
+      $mtx_version    = $this->readUInt8();
+      $mtx_copy_limit = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
+      $mtx_offset_1   = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
+      $mtx_offset_2   = $this->readUInt8() << 16 | $this->readUInt8() << 8 | $this->readUInt8();
+      /*
+      var_dump("$mtx_version $mtx_copy_limit $mtx_offset_1 $mtx_offset_2");
+
+      $pos = $this->pos();
+      $size = $mtx_offset_1 - $pos;
+      var_dump("pos: $pos");
+      var_dump("size: $size");*/
+    }
+
+    if ($flags & self::TTEMBED_XORENCRYPTDATA) {
+      // Process XOR
+    }
+    // TODO Read font data ...
+  }
+
+  /**
+   * Little endian version of the read method
+   *
+   * @param int $n The number of bytes to read
+   *
+   * @return string
+   */
+  public function read($n) {
+    if ($n < 1) {
+      return "";
+    }
+
+    $string = (string) fread($this->f, $n);
+    $chunks = mb_str_split($string, 2, '8bit');
+    $chunks = array_map("strrev", $chunks);
+    return implode("", $chunks);
+  }
+
+  public function readUInt32() {
+    $uint32 = parent::readUInt32();
+
+    return $uint32 >> 16 & 0x0000FFFF | $uint32 << 16 & 0xFFFF0000;
+  }
+
+  /**
+   * Get font copyright
+   *
+   * @return string|null
+   */
+  function getFontCopyright() {
+    return null;
+  }
+
+  /**
+   * Get font name
+   *
+   * @return string|null
+   */
+  function getFontName() {
+    return $this->header->data["FamilyName"];
+  }
+
+  /**
+   * Get font subfamily
+   *
+   * @return string|null
+   */
+  function getFontSubfamily() {
+    return $this->header->data["StyleName"];
+  }
+
+  /**
+   * Get font subfamily ID
+   *
+   * @return string|null
+   */
+  function getFontSubfamilyID() {
+    return $this->header->data["StyleName"];
+  }
+
+  /**
+   * Get font full name
+   *
+   * @return string|null
+   */
+  function getFontFullName() {
+    return $this->header->data["FullName"];
+  }
+
+  /**
+   * Get font version
+   *
+   * @return string|null
+   */
+  function getFontVersion() {
+    return $this->header->data["VersionName"];
+  }
+
+  /**
+   * Get font weight
+   *
+   * @return string|null
+   */
+  function getFontWeight() {
+    return $this->header->data["Weight"];
+  }
+
+  /**
+   * Get font Postscript name
+   *
+   * @return string|null
+   */
+  function getFontPostscriptName() {
+    return null;
+  }
+}

programming_language: PHP
filename: Header.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,113 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\EOT;
+
+use Exception;
+use FontLib\Font;
+
+/**
+ * TrueType font file header.
+ *
+ * @package php-font-lib
+ *
+ * @property File $font
+ */
+class Header extends \FontLib\Header {
+  protected $def = array(
+    "format"        => self::uint32,
+    "numTables"     => self::uint16,
+    "searchRange"   => self::uint16,
+    "entrySelector" => self::uint16,
+    "rangeShift"    => self::uint16,
+  );
+
+  public function parse() {
+    $font = $this->font;
+
+    $this->data = $font->unpack(array(
+      "EOTSize"            => self::uint32,
+      "FontDataSize"       => self::uint32,
+      "Version"            => self::uint32,
+      "Flags"              => self::uint32,
+      "FontPANOSE"         => array(self::uint8, 10),
+      "Charset"            => self::uint8,
+      "Italic"             => self::uint8,
+      "Weight"             => self::uint32,
+      "fsType"             => self::uint16,
+      "MagicNumber"        => self::uint16,
+      "UnicodeRange1"      => self::uint32,
+      "UnicodeRange2"      => self::uint32,
+      "UnicodeRange3"      => self::uint32,
+      "UnicodeRange4"      => self::uint32,
+      "CodePageRange1"     => self::uint32,
+      "CodePageRange2"     => self::uint32,
+      "CheckSumAdjustment" => self::uint32,
+      "Reserved1"          => self::uint32,
+      "Reserved2"          => self::uint32,
+      "Reserved3"          => self::uint32,
+      "Reserved4"          => self::uint32,
+    ));
+
+    $this->data["Padding1"] = $font->readUInt16();
+    $this->readString("FamilyName");
+
+    $this->data["Padding2"] = $font->readUInt16();
+    $this->readString("StyleName");
+
+    $this->data["Padding3"] = $font->readUInt16();
+    $this->readString("VersionName");
+
+    $this->data["Padding4"] = $font->readUInt16();
+    $this->readString("FullName");
+
+    switch ($this->data["Version"]) {
+      default:
+        throw new Exception("Unknown EOT version " . $this->data["Version"]);
+
+      case 0x00010000:
+        // Nothing to do more
+        break;
+
+      case 0x00020001:
+        $this->data["Padding5"] = $font->readUInt16();
+        $this->readString("RootString");
+        break;
+
+      case 0x00020002:
+        $this->data["Padding5"] = $font->readUInt16();
+        $this->readString("RootString");
+
+        $this->data["RootStringCheckSum"] = $font->readUInt32();
+        $this->data["EUDCCodePage"]       = $font->readUInt32();
+
+        $this->data["Padding6"] = $font->readUInt16();
+        $this->readString("Signature");
+
+        $this->data["EUDCFlags"]    = $font->readUInt32();
+        $this->data["EUDCFontSize"] = $font->readUInt32();
+        break;
+    }
+
+    if (!empty($this->data["RootString"])) {
+      $this->data["RootString"] = explode("\0", $this->data["RootString"]);
+    }
+  }
+
+  private function readString($name) {
+    $font = $this->font;
+    $size = $font->readUInt16();
+
+    $this->data["{$name}Size"] = $size;
+    $this->data[$name]         = Font::UTF16ToUTF8($font->read($size));
+  }
+
+  public function encode() {
+    //return $this->font->pack($this->def, $this->data);
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: EncodingMap.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,37 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+/**
+ * Encoding map used to map a code point to a Unicode char.
+ *
+ * @package php-font-lib
+ */
+class EncodingMap {
+  private $f;
+
+  function __construct($file) {
+    $this->f = fopen($file, "r");
+  }
+
+  function parse() {
+    $map = array();
+
+    while ($line = fgets($this->f)) {
+      if (preg_match('/^[\!\=]([0-9A-F]{2,})\s+U\+([0-9A-F]{2})([0-9A-F]{2})\s+([^\s]+)/', $line, $matches)) {
+        $unicode = (hexdec($matches[2]) << 8) + hexdec($matches[3]);
+        $map[hexdec($matches[1])] = array($unicode, $matches[4]);
+      }
+    }
+
+    ksort($map);
+
+    return $map;
+  }
+}

programming_language: PHP
filename: FontNotFoundException.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,11 @@
+<?php
+
+namespace FontLib\Exception;
+
+class FontNotFoundException extends \Exception
+{
+    public function __construct($fontPath)
+    {
+        $this->message = 'Font not found in: ' . $fontPath;
+    }
+}
\ No newline at end of file

programming_language: PHP
filename: Font.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,89 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib;
+
+use FontLib\Exception\FontNotFoundException;
+
+/**
+ * Generic font file.
+ *
+ * @package php-font-lib
+ */
+class Font {
+  static $debug = false;
+
+  /**
+   * @param string $file The font file
+   *
+   * @return TrueType\File|null $file
+   */
+  public static function load($file) {
+      if(!file_exists($file)){
+          throw new FontNotFoundException($file);
+      }
+
+    $header = file_get_contents($file, false, null, 0, 4);
+    $class  = null;
+
+    switch ($header) {
+      case "\x00\x01\x00\x00":
+      case "true":
+      case "typ1":
+        $class = "TrueType\\File";
+        break;
+
+      case "OTTO":
+        $class = "OpenType\\File";
+        break;
+
+      case "wOFF":
+        $class = "WOFF\\File";
+        break;
+
+      case "ttcf":
+        $class = "TrueType\\Collection";
+        break;
+
+      // Unknown type or EOT
+      default:
+        $magicNumber = file_get_contents($file, false, null, 34, 2);
+
+        if ($magicNumber === "LP") {
+          $class = "EOT\\File";
+        }
+    }
+
+    if ($class) {
+      $class = "FontLib\\$class";
+
+      /** @var TrueType\File $obj */
+      $obj = new $class;
+      $obj->load($file);
+
+      return $obj;
+    }
+
+    return null;
+  }
+
+  static function d($str) {
+    if (!self::$debug) {
+      return;
+    }
+    echo "$str\n";
+  }
+
+  static function UTF16ToUTF8($str) {
+    return mb_convert_encoding($str, "utf-8", "utf-16");
+  }
+
+  static function UTF8ToUTF16($str) {
+    return mb_convert_encoding($str, "utf-16", "utf-8");
+  }
+}

programming_language: PHP
filename: Outline.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,109 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
+ */
+namespace FontLib\Glyph;
+
+use FontLib\Table\Type\glyf;
+use FontLib\TrueType\File;
+use FontLib\BinaryStream;
+
+/**
+ * `glyf` font table.
+ *
+ * @package php-font-lib
+ */
+class Outline extends BinaryStream {
+  /**
+   * @var \FontLib\Table\Type\glyf
+   */
+  protected $table;
+
+  protected $offset;
+  protected $size;
+
+  // Data
+  public $numberOfContours;
+  public $xMin;
+  public $yMin;
+  public $xMax;
+  public $yMax;
+
+  /**
+   * @var string|null
+   */
+  public $raw;
+
+  /**
+   * @param glyf $table
+   * @param                 $offset
+   * @param                 $size
+   *
+   * @return Outline
+   */
+  static function init(glyf $table, $offset, $size, BinaryStream $font) {
+    $font->seek($offset);
+
+    if ($font->readInt16() > -1) {
+      /** @var OutlineSimple $glyph */
+      $glyph = new OutlineSimple($table, $offset, $size);
+    }
+    else {
+      /** @var OutlineComposite $glyph */
+      $glyph = new OutlineComposite($table, $offset, $size);
+    }
+
+    $glyph->parse($font);
+
+    return $glyph;
+  }
+
+  /**
+   * @return File
+   */
+  function getFont() {
+    return $this->table->getFont();
+  }
+
+  function __construct(glyf $table, $offset = null, $size = null) {
+    $this->table  = $table;
+    $this->offset = $offset;
+    $this->size   = $size;
+  }
+
+  function parse(BinaryStream $font) {
+    $font->seek($this->offset);
+
+      $this->raw = $font->read($this->size);
+  }
+
+  function parseData() {
+    $font = $this->getFont();
+    $font->seek($this->offset);
+
+    $this->numberOfContours = $font->readInt16();
+    $this->xMin             = $font->readFWord();
+    $this->yMin             = $font->readFWord();
+    $this->xMax             = $font->readFWord();
+    $this->yMax             = $font->readFWord();
+  }
+
+  function encode() {
+    $font = $this->getFont();
+
+    return $font->write($this->raw, mb_strlen((string) $this->raw, '8bit'));
+  }
+
+  function getSVGContours() {
+    // Inherit
+  }
+
+  function getGlyphIDs() {
+    return array();
+  }
+}
+

programming_language: PHP
filename: OutlineComponent.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,31 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
+ */
+
+namespace FontLib\Glyph;
+/**
+ * Glyph outline component
+ *
+ * @package php-font-lib
+ */
+class OutlineComponent {
+  public $flags;
+  public $glyphIndex;
+  public $a, $b, $c, $d, $e, $f;
+  public $point_compound;
+  public $point_component;
+  public $instructions;
+
+  function getMatrix() {
+    return array(
+      $this->a, $this->b,
+      $this->c, $this->d,
+      $this->e, $this->f,
+    );
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: OutlineComposite.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,242 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
+ */
+
+namespace FontLib\Glyph;
+
+/**
+ * Composite glyph outline
+ *
+ * @package php-font-lib
+ */
+class OutlineComposite extends Outline {
+  const ARG_1_AND_2_ARE_WORDS    = 0x0001;
+  const ARGS_ARE_XY_VALUES       = 0x0002;
+  const ROUND_XY_TO_GRID         = 0x0004;
+  const WE_HAVE_A_SCALE          = 0x0008;
+  const MORE_COMPONENTS          = 0x0020;
+  const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
+  const WE_HAVE_A_TWO_BY_TWO     = 0x0080;
+  const WE_HAVE_INSTRUCTIONS     = 0x0100;
+  const USE_MY_METRICS           = 0x0200;
+  const OVERLAP_COMPOUND         = 0x0400;
+
+  /**
+   * @var OutlineComponent[]
+   */
+  public $components = array();
+
+  function getGlyphIDs() {
+    if (empty($this->components)) {
+      $this->parseData();
+    }
+
+    $glyphIDs = array();
+    foreach ($this->components as $_component) {
+      $glyphIDs[] = $_component->glyphIndex;
+
+      $_glyph   = $this->table->data[$_component->glyphIndex];
+
+      if ($_glyph !== $this) {
+        $glyphIDs = array_merge($glyphIDs, $_glyph->getGlyphIDs());
+      }
+    }
+
+    return $glyphIDs;
+  }
+
+  /*function parse() {
+    //$this->parseData();
+  }*/
+
+  function parseData() {
+    parent::parseData();
+
+    $font = $this->getFont();
+
+    do {
+      $flags      = $font->readUInt16();
+      $glyphIndex = $font->readUInt16();
+
+      $a = 1.0;
+      $b = 0.0;
+      $c = 0.0;
+      $d = 1.0;
+      $e = 0.0;
+      $f = 0.0;
+
+      $point_compound  = null;
+      $point_component = null;
+
+      $instructions = null;
+
+      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
+        if ($flags & self::ARGS_ARE_XY_VALUES) {
+          $e = $font->readInt16();
+          $f = $font->readInt16();
+        }
+        else {
+          $point_compound  = $font->readUInt16();
+          $point_component = $font->readUInt16();
+        }
+      }
+      else {
+        if ($flags & self::ARGS_ARE_XY_VALUES) {
+          $e = $font->readInt8();
+          $f = $font->readInt8();
+        }
+        else {
+          $point_compound  = $font->readUInt8();
+          $point_component = $font->readUInt8();
+        }
+      }
+
+      if ($flags & self::WE_HAVE_A_SCALE) {
+        $a = $d = $font->readInt16();
+      }
+      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
+        $a = $font->readInt16();
+        $d = $font->readInt16();
+      }
+      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
+        $a = $font->readInt16();
+        $b = $font->readInt16();
+        $c = $font->readInt16();
+        $d = $font->readInt16();
+      }
+
+      //if ($flags & self::WE_HAVE_INSTRUCTIONS) {
+      //
+      //}
+
+      $component                  = new OutlineComponent();
+      $component->flags           = $flags;
+      $component->glyphIndex      = $glyphIndex;
+      $component->a               = $a;
+      $component->b               = $b;
+      $component->c               = $c;
+      $component->d               = $d;
+      $component->e               = $e;
+      $component->f               = $f;
+      $component->point_compound  = $point_compound;
+      $component->point_component = $point_component;
+      $component->instructions    = $instructions;
+
+      $this->components[] = $component;
+    } while ($flags & self::MORE_COMPONENTS);
+  }
+
+  function encode() {
+    $font = $this->getFont();
+
+    $gids = $font->getSubset();
+
+    $size = $font->writeInt16(-1);
+    $size += $font->writeFWord($this->xMin);
+    $size += $font->writeFWord($this->yMin);
+    $size += $font->writeFWord($this->xMax);
+    $size += $font->writeFWord($this->yMax);
+
+    foreach ($this->components as $_i => $_component) {
+      $flags = 0;
+      if ($_component->point_component === null && $_component->point_compound === null) {
+        $flags |= self::ARGS_ARE_XY_VALUES;
+
+        if (abs($_component->e) > 0x7F || abs($_component->f) > 0x7F) {
+          $flags |= self::ARG_1_AND_2_ARE_WORDS;
+        }
+      }
+      elseif ($_component->point_component > 0xFF || $_component->point_compound > 0xFF) {
+        $flags |= self::ARG_1_AND_2_ARE_WORDS;
+      }
+
+      if ($_component->b == 0 && $_component->c == 0) {
+        if ($_component->a == $_component->d) {
+          if ($_component->a != 1.0) {
+            $flags |= self::WE_HAVE_A_SCALE;
+          }
+        }
+        else {
+          $flags |= self::WE_HAVE_AN_X_AND_Y_SCALE;
+        }
+      }
+      else {
+        $flags |= self::WE_HAVE_A_TWO_BY_TWO;
+      }
+
+      if ($_i < count($this->components) - 1) {
+        $flags |= self::MORE_COMPONENTS;
+      }
+
+      $size += $font->writeUInt16($flags);
+
+      $new_gid = array_search($_component->glyphIndex, $gids);
+      $size += $font->writeUInt16($new_gid);
+
+      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
+        if ($flags & self::ARGS_ARE_XY_VALUES) {
+          $size += $font->writeInt16($_component->e);
+          $size += $font->writeInt16($_component->f);
+        }
+        else {
+          $size += $font->writeUInt16($_component->point_compound);
+          $size += $font->writeUInt16($_component->point_component);
+        }
+      }
+      else {
+        if ($flags & self::ARGS_ARE_XY_VALUES) {
+          $size += $font->writeInt8($_component->e);
+          $size += $font->writeInt8($_component->f);
+        }
+        else {
+          $size += $font->writeUInt8($_component->point_compound);
+          $size += $font->writeUInt8($_component->point_component);
+        }
+      }
+
+      if ($flags & self::WE_HAVE_A_SCALE) {
+        $size += $font->writeInt16($_component->a);
+      }
+      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
+        $size += $font->writeInt16($_component->a);
+        $size += $font->writeInt16($_component->d);
+      }
+      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
+        $size += $font->writeInt16($_component->a);
+        $size += $font->writeInt16($_component->b);
+        $size += $font->writeInt16($_component->c);
+        $size += $font->writeInt16($_component->d);
+      }
+    }
+
+    return $size;
+  }
+
+  public function getSVGContours() {
+    $contours = array();
+
+    /** @var \FontLib\Table\Type\glyf $glyph_data */
+    $glyph_data = $this->getFont()->getTableObject("glyf");
+
+    /** @var Outline[] $glyphs */
+    $glyphs = $glyph_data->data;
+
+    foreach ($this->components as $component) {
+      $_glyph = $glyphs[$component->glyphIndex];
+
+      if ($_glyph !== $this) {
+        $contours[] = array(
+          "contours"  => $_glyph->getSVGContours(),
+          "transform" => $component->getMatrix(),
+        );
+      }
+    }
+
+    return $contours;
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: OutlineSimple.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,335 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
+ */
+
+namespace FontLib\Glyph;
+
+/**
+ * `glyf` font table.
+ *
+ * @package php-font-lib
+ */
+class OutlineSimple extends Outline {
+  const ON_CURVE       = 0x01;
+  const X_SHORT_VECTOR = 0x02;
+  const Y_SHORT_VECTOR = 0x04;
+  const REPEAT         = 0x08;
+  const THIS_X_IS_SAME = 0x10;
+  const THIS_Y_IS_SAME = 0x20;
+
+  public $instructions;
+  public $points;
+
+  function parseData() {
+    parent::parseData();
+
+    if (!$this->size) {
+      return;
+    }
+
+    $font = $this->getFont();
+
+    $noc = $this->numberOfContours;
+
+    if ($noc == 0) {
+      return;
+    }
+
+    $endPtsOfContours = $font->r(array(self::uint16, $noc));
+
+    $instructionLength  = $font->readUInt16();
+    $this->instructions = $font->r(array(self::uint8, $instructionLength));
+
+    $count = $endPtsOfContours[$noc - 1] + 1;
+
+    // Flags
+    $flags = array();
+    for ($index = 0; $index < $count; $index++) {
+      $flags[$index] = $font->readUInt8();
+
+      if ($flags[$index] & self::REPEAT) {
+        $repeats = $font->readUInt8();
+
+        for ($i = 1; $i <= $repeats; $i++) {
+          $flags[$index + $i] = $flags[$index];
+        }
+
+        $index += $repeats;
+      }
+    }
+
+    $points = array();
+    foreach ($flags as $i => $flag) {
+      $points[$i]["onCurve"]      = $flag & self::ON_CURVE;
+      $points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
+    }
+
+    // X Coords
+    $x = 0;
+    for ($i = 0; $i < $count; $i++) {
+      $flag = $flags[$i];
+
+      if ($flag & self::THIS_X_IS_SAME) {
+        if ($flag & self::X_SHORT_VECTOR) {
+          $x += $font->readUInt8();
+        }
+      }
+      else {
+        if ($flag & self::X_SHORT_VECTOR) {
+          $x -= $font->readUInt8();
+        }
+        else {
+          $x += $font->readInt16();
+        }
+      }
+
+      $points[$i]["x"] = $x;
+    }
+
+    // Y Coords
+    $y = 0;
+    for ($i = 0; $i < $count; $i++) {
+      $flag = $flags[$i];
+
+      if ($flag & self::THIS_Y_IS_SAME) {
+        if ($flag & self::Y_SHORT_VECTOR) {
+          $y += $font->readUInt8();
+        }
+      }
+      else {
+        if ($flag & self::Y_SHORT_VECTOR) {
+          $y -= $font->readUInt8();
+        }
+        else {
+          $y += $font->readInt16();
+        }
+      }
+
+      $points[$i]["y"] = $y;
+    }
+
+    $this->points = $points;
+  }
+
+  public function splitSVGPath($path) {
+    preg_match_all('/([a-z])|(-?\d+(?:\.\d+)?)/i', $path, $matches, PREG_PATTERN_ORDER);
+
+    return $matches[0];
+  }
+
+  public function makePoints($path) {
+    $path = $this->splitSVGPath($path);
+    $l    = count($path);
+    $i    = 0;
+
+    $points = array();
+
+    while ($i < $l) {
+      switch ($path[$i]) {
+        // moveTo
+        case "M":
+          $points[] = array(
+            "onCurve"      => true,
+            "x"            => $path[++$i],
+            "y"            => $path[++$i],
+            "endOfContour" => false,
+          );
+          break;
+
+        // lineTo
+        case "L":
+          $points[] = array(
+            "onCurve"      => true,
+            "x"            => $path[++$i],
+            "y"            => $path[++$i],
+            "endOfContour" => false,
+          );
+          break;
+
+        // quadraticCurveTo
+        case "Q":
+          $points[] = array(
+            "onCurve"      => false,
+            "x"            => $path[++$i],
+            "y"            => $path[++$i],
+            "endOfContour" => false,
+          );
+          $points[] = array(
+            "onCurve"      => true,
+            "x"            => $path[++$i],
+            "y"            => $path[++$i],
+            "endOfContour" => false,
+          );
+          break;
+
+        // closePath
+        /** @noinspection PhpMissingBreakStatementInspection */
+        case "z":
+          $points[count($points) - 1]["endOfContour"] = true;
+
+        default:
+          $i++;
+          break;
+      }
+    }
+
+    return $points;
+  }
+
+  function encode() {
+    if (empty($this->points)) {
+      return parent::encode();
+    }
+
+    return $this->size = $this->encodePoints($this->points);
+  }
+
+  public function encodePoints($points) {
+    $endPtsOfContours = array();
+    $flags            = array();
+    $coords_x         = array();
+    $coords_y         = array();
+
+    $last_x = 0;
+    $last_y = 0;
+    $xMin   = $yMin = 0xFFFF;
+    $xMax   = $yMax = -0xFFFF;
+    foreach ($points as $i => $point) {
+      $flag = 0;
+      if ($point["onCurve"]) {
+        $flag |= self::ON_CURVE;
+      }
+
+      if ($point["endOfContour"]) {
+        $endPtsOfContours[] = $i;
+      }
+
+      // Simplified, we could do some optimizations
+      if ($point["x"] == $last_x) {
+        $flag |= self::THIS_X_IS_SAME;
+      }
+      else {
+        $x          = intval($point["x"]);
+        $xMin       = min($x, $xMin);
+        $xMax       = max($x, $xMax);
+        $coords_x[] = $x - $last_x; // int16
+      }
+
+      // Simplified, we could do some optimizations
+      if ($point["y"] == $last_y) {
+        $flag |= self::THIS_Y_IS_SAME;
+      }
+      else {
+        $y          = intval($point["y"]);
+        $yMin       = min($y, $yMin);
+        $yMax       = max($y, $yMax);
+        $coords_y[] = $y - $last_y; // int16
+      }
+
+      $flags[] = $flag;
+      $last_x  = $point["x"];
+      $last_y  = $point["y"];
+    }
+
+    $font = $this->getFont();
+
+    $l = 0;
+    $l += $font->writeInt16(count($endPtsOfContours)); // endPtsOfContours
+    $l += $font->writeFWord(isset($this->xMin) ? $this->xMin : $xMin); // xMin
+    $l += $font->writeFWord(isset($this->yMin) ? $this->yMin : $yMin); // yMin
+    $l += $font->writeFWord(isset($this->xMax) ? $this->xMax : $xMax); // xMax
+    $l += $font->writeFWord(isset($this->yMax) ? $this->yMax : $yMax); // yMax
+
+    // Simple glyf
+    $l += $font->w(array(self::uint16, count($endPtsOfContours)), $endPtsOfContours); // endPtsOfContours
+    $l += $font->writeUInt16(0); // instructionLength
+    $l += $font->w(array(self::uint8, count($flags)), $flags); // flags
+    $l += $font->w(array(self::int16, count($coords_x)), $coords_x); // xCoordinates
+    $l += $font->w(array(self::int16, count($coords_y)), $coords_y); // yCoordinates
+    return $l;
+  }
+
+  public function getSVGContours($points = null) {
+    $path = "";
+
+    if (!$points) {
+      if (empty($this->points)) {
+        $this->parseData();
+      }
+
+      $points = $this->points;
+    }
+
+    $length     = (empty($points) ? 0 : count($points));
+    $firstIndex = 0;
+    $count      = 0;
+
+    for ($i = 0; $i < $length; $i++) {
+      $count++;
+
+      if ($points[$i]["endOfContour"]) {
+        $path .= $this->getSVGPath($points, $firstIndex, $count);
+        $firstIndex = $i + 1;
+        $count      = 0;
+      }
+    }
+
+    return $path;
+  }
+
+  protected function getSVGPath($points, $startIndex, $count) {
+    $offset = 0;
+    $path   = "";
+
+    while ($offset < $count) {
+      $point    = $points[$startIndex + $offset % $count];
+      $point_p1 = $points[$startIndex + ($offset + 1) % $count];
+
+      if ($offset == 0) {
+        $path .= "M{$point['x']},{$point['y']} ";
+      }
+
+      if ($point["onCurve"]) {
+        if ($point_p1["onCurve"]) {
+          $path .= "L{$point_p1['x']},{$point_p1['y']} ";
+          $offset++;
+        }
+        else {
+          $point_p2 = $points[$startIndex + ($offset + 2) % $count];
+
+          if ($point_p2["onCurve"]) {
+            $path .= "Q{$point_p1['x']},{$point_p1['y']},{$point_p2['x']},{$point_p2['y']} ";
+          }
+          else {
+            $path .= "Q{$point_p1['x']},{$point_p1['y']}," . $this->midValue($point_p1['x'], $point_p2['x']) . "," . $this->midValue($point_p1['y'], $point_p2['y']) . " ";
+          }
+
+          $offset += 2;
+        }
+      }
+      else {
+        if ($point_p1["onCurve"]) {
+          $path .= "Q{$point['x']},{$point['y']},{$point_p1['x']},{$point_p1['y']} ";
+        }
+        else {
+          $path .= "Q{$point['x']},{$point['y']}," . $this->midValue($point['x'], $point_p1['x']) . "," . $this->midValue($point['y'], $point_p1['y']) . " ";
+        }
+
+        $offset++;
+      }
+    }
+
+    $path .= "z ";
+
+    return $path;
+  }
+
+  function midValue($a, $b) {
+    return $a + ($b - $a) / 2;
+  }
+}

programming_language: PHP
filename: Header.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,37 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+namespace FontLib;
+
+use FontLib\TrueType\File;
+
+/**
+ * Font header container.
+ *
+ * @package php-font-lib
+ */
+abstract class Header extends BinaryStream {
+  /**
+   * @var File
+   */
+  protected $font;
+  protected $def = array();
+
+  public $data;
+
+  public function __construct(File $font) {
+    $this->font = $font;
+  }
+
+  public function encode() {
+    return $this->font->pack($this->def, $this->data);
+  }
+
+  public function parse() {
+    $this->data = $this->font->unpack($this->def);
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: File.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,18 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\OpenType;
+
+/**
+ * Open Type font, the same as a TrueType one.
+ *
+ * @package php-font-lib
+ */
+class File extends \FontLib\TrueType\File {
+  //
+}

programming_language: PHP
filename: TableDirectoryEntry.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,18 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\OpenType;
+
+/**
+ * Open Type Table directory entry, the same as a TrueType one.
+ *
+ * @package php-font-lib
+ */
+class TableDirectoryEntry extends \FontLib\TrueType\TableDirectoryEntry {
+
+}

programming_language: PHP
filename: DirectoryEntry.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,134 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+namespace FontLib\Table;
+
+use FontLib\TrueType\File;
+use FontLib\Font;
+use FontLib\BinaryStream;
+
+/**
+ * Generic Font table directory entry.
+ *
+ * @package php-font-lib
+ */
+class DirectoryEntry extends BinaryStream {
+  /**
+   * @var File
+   */
+  protected $font;
+
+  /**
+   * @var Table
+   */
+  protected $font_table;
+
+  public $entryLength = 4;
+
+  public $tag;
+  public $checksum;
+  public $offset;
+  public $length;
+
+  protected $origF;
+
+  /**
+   * @param string $data
+   *
+   * @return int
+   */
+  static function computeChecksum($data) {
+    $len = mb_strlen($data, '8bit');
+    $mod = $len % 4;
+
+    if ($mod) {
+      $data = str_pad($data, $len + (4 - $mod), "\0");
+    }
+
+    $len = mb_strlen($data, '8bit');
+
+    $hi = 0x0000;
+    $lo = 0x0000;
+
+    for ($i = 0; $i < $len; $i += 4) {
+      $hi += (ord($data[$i]) << 8) + ord($data[$i + 1]);
+      $lo += (ord($data[$i + 2]) << 8) + ord($data[$i + 3]);
+      $hi += $lo >> 16;
+      $lo = $lo & 0xFFFF;
+      $hi = $hi & 0xFFFF;
+    }
+
+    return ($hi << 8) + $lo;
+  }
+
+  function __construct(File $font) {
+    $this->font = $font;
+    $this->f    = $font->f;
+  }
+
+  function parse() {
+    $this->tag = $this->font->read(4);
+  }
+
+  function open($filename, $mode = self::modeRead) {
+    // void
+  }
+
+  function setTable(Table $font_table) {
+    $this->font_table = $font_table;
+  }
+
+  function encode($entry_offset) {
+    Font::d("\n==== $this->tag ====");
+    //Font::d("Entry offset  = $entry_offset");
+
+    $data = $this->font_table;
+    $font = $this->font;
+
+    $table_offset = $font->pos();
+    $this->offset = $table_offset;
+    $table_length = $data->encode();
+
+    $font->seek($table_offset);
+    $table_data = $font->read($table_length);
+
+    $font->seek($entry_offset);
+
+    $font->write($this->tag, 4);
+    $font->writeUInt32(self::computeChecksum($table_data));
+    $font->writeUInt32($table_offset);
+    $font->writeUInt32($table_length);
+
+    Font::d("Bytes written = $table_length");
+
+    $font->seek($table_offset + $table_length);
+  }
+
+  /**
+   * @return File
+   */
+  function getFont() {
+    return $this->font;
+  }
+
+  function startRead() {
+    $this->font->seek($this->offset);
+  }
+
+  function endRead() {
+    //
+  }
+
+  function startWrite() {
+    $this->font->seek($this->offset);
+  }
+
+  function endWrite() {
+    //
+  }
+}
+

programming_language: PHP
filename: Table.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,93 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+namespace FontLib\Table;
+
+use FontLib\TrueType\File;
+use FontLib\Font;
+use FontLib\BinaryStream;
+
+/**
+ * Generic font table.
+ *
+ * @package php-font-lib
+ */
+class Table extends BinaryStream {
+  /**
+   * @var DirectoryEntry
+   */
+  protected $entry;
+  protected $def = array();
+
+  public $data;
+
+  final public function __construct(DirectoryEntry $entry) {
+    $this->entry = $entry;
+    $entry->setTable($this);
+  }
+
+  /**
+   * @return File
+   */
+  public function getFont() {
+    return $this->entry->getFont();
+  }
+
+  protected function _encode() {
+    if (empty($this->data)) {
+      Font::d("  >> Table is empty");
+
+      return 0;
+    }
+
+    return $this->getFont()->pack($this->def, $this->data);
+  }
+
+  protected function _parse() {
+    $this->data = $this->getFont()->unpack($this->def);
+  }
+
+  protected function _parseRaw() {
+    $this->data = $this->getFont()->read($this->entry->length);
+  }
+
+  protected function _encodeRaw() {
+    return $this->getFont()->write($this->data, $this->entry->length);
+  }
+
+  public function toHTML() {
+    return "<pre>" . var_export($this->data, true) . "</pre>";
+  }
+
+  final public function encode() {
+    $this->entry->startWrite();
+
+    if (false && empty($this->def)) {
+      $length = $this->_encodeRaw();
+    }
+    else {
+      $length = $this->_encode();
+    }
+
+    $this->entry->endWrite();
+
+    return $length;
+  }
+
+  final public function parse() {
+    $this->entry->startRead();
+
+    if (false && empty($this->def)) {
+      $this->_parseRaw();
+    }
+    else {
+      $this->_parse();
+    }
+
+    $this->entry->endRead();
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: cmap.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,298 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `cmap` font table.
+ *
+ * @package php-font-lib
+ */
+class cmap extends Table {
+  private static $header_format = array(
+    "version"         => self::uint16,
+    "numberSubtables" => self::uint16,
+  );
+
+  private static $subtable_header_format = array(
+    "platformID"         => self::uint16,
+    "platformSpecificID" => self::uint16,
+    "offset"             => self::uint32,
+  );
+
+  private static $subtable_v4_format = array(
+    "length"        => self::uint16,
+    "language"      => self::uint16,
+    "segCountX2"    => self::uint16,
+    "searchRange"   => self::uint16,
+    "entrySelector" => self::uint16,
+    "rangeShift"    => self::uint16,
+  );
+
+  private static $subtable_v12_format = array(
+    "length"        => self::uint32,
+    "language"      => self::uint32,
+    "ngroups"    => self::uint32
+  );
+
+  protected function _parse() {
+    $font = $this->getFont();
+
+    $cmap_offset = $font->pos();
+
+    $data = $font->unpack(self::$header_format);
+
+    $subtables = array();
+    for ($i = 0; $i < $data["numberSubtables"]; $i++) {
+      $subtables[] = $font->unpack(self::$subtable_header_format);
+    }
+
+    $data["subtables"] = $subtables;
+
+    foreach ($data["subtables"] as $i => &$subtable) {
+      $font->seek($cmap_offset + $subtable["offset"]);
+
+      $subtable["format"] = $font->readUInt16();
+
+      // @todo Only CMAP version 4 and 12
+      if (($subtable["format"] != 4) && ($subtable["format"] != 12)) {
+        unset($data["subtables"][$i]);
+        $data["numberSubtables"]--;
+        continue;
+      }
+
+      if ($subtable["format"] == 12) {
+
+        $font->readUInt16();
+
+        $subtable += $font->unpack(self::$subtable_v12_format);
+
+        $glyphIndexArray = array();
+        $endCodes = array();
+        $startCodes = array();
+
+        for ($p = 0; $p < $subtable['ngroups']; $p++) {
+
+          $startCode = $startCodes[] = $font->readUInt32();
+          $endCode = $endCodes[] = $font->readUInt32();
+          $startGlyphCode = $font->readUInt32();
+
+          for ($c = $startCode; $c <= $endCode; $c++) {
+            $glyphIndexArray[$c] = $startGlyphCode;
+            $startGlyphCode++;
+          }
+        }
+
+        $subtable += array(
+          "startCode" => $startCodes,
+          "endCode" => $endCodes,
+          "glyphIndexArray" => $glyphIndexArray,
+        );
+
+      }
+      else if ($subtable["format"] == 4) {
+
+        $subtable += $font->unpack(self::$subtable_v4_format);
+
+        $segCount             = $subtable["segCountX2"] / 2;
+        $subtable["segCount"] = $segCount;
+
+        $endCode = $font->readUInt16Many($segCount);
+
+        $font->readUInt16(); // reservedPad
+
+        $startCode = $font->readUInt16Many($segCount);
+        $idDelta   = $font->readInt16Many($segCount);
+
+        $ro_start      = $font->pos();
+        $idRangeOffset = $font->readUInt16Many($segCount);
+
+        $glyphIndexArray = array();
+        for ($i = 0; $i < $segCount; $i++) {
+          $c1 = $startCode[$i];
+          $c2 = $endCode[$i];
+          $d  = $idDelta[$i];
+          $ro = $idRangeOffset[$i];
+
+          if ($ro > 0) {
+            $font->seek($subtable["offset"] + 2 * $i + $ro);
+          }
+
+          for ($c = $c1; $c <= $c2; $c++) {
+            if ($ro == 0) {
+              $gid = ($c + $d) & 0xFFFF;
+            }
+            else {
+              $offset = ($c - $c1) * 2 + $ro;
+              $offset = $ro_start + 2 * $i + $offset;
+
+              $font->seek($offset);
+              $gid = $font->readUInt16();
+
+              if ($gid != 0) {
+                $gid = ($gid + $d) & 0xFFFF;
+              }
+            }
+
+            if ($gid > 0) {
+              $glyphIndexArray[$c] = $gid;
+            }
+          }
+        }
+
+        $subtable += array(
+          "endCode"         => $endCode,
+          "startCode"       => $startCode,
+          "idDelta"         => $idDelta,
+          "idRangeOffset"   => $idRangeOffset,
+          "glyphIndexArray" => $glyphIndexArray,
+        );
+      }
+    }
+
+    $this->data = $data;
+  }
+
+  function _encode() {
+    $font = $this->getFont();
+
+    $subset          = $font->getSubset();
+    $glyphIndexArray = $font->getUnicodeCharMap();
+
+    $newGlyphIndexArray = array();
+    foreach ($glyphIndexArray as $code => $gid) {
+      $new_gid = array_search($gid, $subset);
+      if ($new_gid !== false) {
+        $newGlyphIndexArray[$code] = $new_gid;
+      }
+    }
+
+    ksort($newGlyphIndexArray); // Sort by char code
+
+    $segments = array();
+
+    $i        = -1;
+    $prevCode = 0xFFFF;
+    $prevGid  = 0xFFFF;
+
+    foreach ($newGlyphIndexArray as $code => $gid) {
+      if (
+        $prevCode + 1 != $code ||
+        $prevGid + 1 != $gid
+      ) {
+        $i++;
+        $segments[$i] = array();
+      }
+
+      $segments[$i][] = array($code, $gid);
+
+      $prevCode = $code;
+      $prevGid  = $gid;
+    }
+
+    $segments[][] = array(0xFFFF, 0xFFFF);
+
+    $startCode = array();
+    $endCode   = array();
+    $idDelta   = array();
+
+    foreach ($segments as $codes) {
+      $start = reset($codes);
+      $end   = end($codes);
+
+      $startCode[] = $start[0];
+      $endCode[]   = $end[0];
+      $idDelta[]   = $start[1] - $start[0];
+    }
+
+    $segCount      = count($startCode);
+    $idRangeOffset = array_fill(0, $segCount, 0);
+
+    $searchRange   = 1;
+    $entrySelector = 0;
+    while ($searchRange * 2 <= $segCount) {
+      $searchRange *= 2;
+      $entrySelector++;
+    }
+    $searchRange *= 2;
+    $rangeShift = $segCount * 2 - $searchRange;
+
+    $subtables = array(
+      array(
+        // header
+        "platformID"         => 3, // Unicode
+        "platformSpecificID" => 1,
+        "offset"             => null,
+
+        // subtable
+        "format"             => 4,
+        "length"             => null,
+        "language"           => 0,
+        "segCount"           => $segCount,
+        "segCountX2"         => $segCount * 2,
+        "searchRange"        => $searchRange,
+        "entrySelector"      => $entrySelector,
+        "rangeShift"         => $rangeShift,
+        "startCode"          => $startCode,
+        "endCode"            => $endCode,
+        "idDelta"            => $idDelta,
+        "idRangeOffset"      => $idRangeOffset,
+        "glyphIndexArray"    => $newGlyphIndexArray,
+      )
+    );
+
+    $data = array(
+      "version"         => 0,
+      "numberSubtables" => count($subtables),
+      "subtables"       => $subtables,
+    );
+
+    $length = $font->pack(self::$header_format, $data);
+
+    $subtable_headers_size   = $data["numberSubtables"] * 8; // size of self::$subtable_header_format
+    $subtable_headers_offset = $font->pos();
+
+    $length += $font->write(str_repeat("\0", $subtable_headers_size), $subtable_headers_size);
+
+    // write subtables data
+    foreach ($data["subtables"] as $i => $subtable) {
+      $length_before                   = $length;
+      $data["subtables"][$i]["offset"] = $length;
+
+      $length += $font->writeUInt16($subtable["format"]);
+
+      $before_subheader = $font->pos();
+      $length += $font->pack(self::$subtable_v4_format, $subtable);
+
+      $segCount = $subtable["segCount"];
+      $length += $font->w(array(self::uint16, $segCount), $subtable["endCode"]);
+      $length += $font->writeUInt16(0); // reservedPad
+      $length += $font->w(array(self::uint16, $segCount), $subtable["startCode"]);
+      $length += $font->w(array(self::int16, $segCount), $subtable["idDelta"]);
+      $length += $font->w(array(self::uint16, $segCount), $subtable["idRangeOffset"]);
+      $length += $font->w(array(self::uint16, $segCount), array_values($subtable["glyphIndexArray"]));
+
+      $after_subtable = $font->pos();
+
+      $subtable["length"] = $length - $length_before;
+      $font->seek($before_subheader);
+      $length += $font->pack(self::$subtable_v4_format, $subtable);
+
+      $font->seek($after_subtable);
+    }
+
+    // write subtables headers
+    $font->seek($subtable_headers_offset);
+    foreach ($data["subtables"] as $subtable) {
+      $font->pack(self::$subtable_header_format, $subtable);
+    }
+
+    return $length;
+  }
+}

programming_language: PHP
filename: glyf.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,154 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+
+use FontLib\Table\Table;
+use FontLib\Glyph\Outline;
+use FontLib\Glyph\OutlineSimple;
+
+/**
+ * `glyf` font table.
+ *
+ * @package php-font-lib
+ * @property Outline[] $data
+ */
+class glyf extends Table {
+  protected function _parse() {
+    $font   = $this->getFont();
+    $offset = $font->pos();
+
+    $loca      = $font->getData("loca");
+    $real_loca = array_slice($loca, 0, -1); // Not the last dummy loca entry
+
+    $data = array();
+
+    foreach ($real_loca as $gid => $location) {
+      $_offset    = $offset + $loca[$gid];
+      $_size      = $loca[$gid + 1] - $loca[$gid];
+      $data[$gid] = Outline::init($this, $_offset, $_size, $font);
+    }
+
+    $this->data = $data;
+  }
+
+  public function getGlyphIDs($gids = array()) {
+    $glyphIDs = array();
+
+    foreach ($gids as $_gid) {
+      $_glyph   = $this->data[$_gid];
+      $glyphIDs = array_merge($glyphIDs, $_glyph->getGlyphIDs());
+    }
+
+    return array_unique(array_merge($gids, $glyphIDs));
+  }
+
+  public function toHTML() {
+    $max  = 160;
+    $font = $this->getFont();
+
+    $head      = $font->getData("head");
+    $head_json = json_encode($head);
+
+    $os2      = $font->getData("OS/2");
+    $os2_json = json_encode($os2);
+
+    $hmtx      = $font->getData("hmtx");
+    $hmtx_json = json_encode($hmtx);
+
+    $names           = $font->getData("post", "names");
+    $glyphIndexArray = array_flip($font->getUnicodeCharMap());
+
+    $width  = (abs($head["xMin"]) + $head["xMax"]);
+    $height = (abs($head["yMin"]) + $head["yMax"]);
+
+    $ratio = 1;
+    if ($width > $max || $height > $max) {
+      $ratio  = max($width, $height) / $max;
+      $width  = round($width / $ratio);
+      $height = round($height / $ratio);
+    }
+
+    $n = 500;
+
+    $s = "<h3>" . "Only the first $n simple glyphs are shown (" . count($this->data) . " total)
+    <div class='glyph-view simple'>Simple glyph</div>
+    <div class='glyph-view composite'>Composite glyph</div>
+    Zoom: <input type='range' value='100' max='400' onchange='Glyph.resize(this.value)' />
+    </h3>
+    <script>
+      Glyph.ratio  = $ratio;
+      Glyph.head   = $head_json;
+      Glyph.os2    = $os2_json;
+      Glyph.hmtx   = $hmtx_json;
+      Glyph.width  = $width;
+      Glyph.height = $height;
+    </script>";
+
+    foreach ($this->data as $g => $glyph) {
+      if ($n-- <= 0) {
+        break;
+      }
+
+      $glyph->parseData();
+
+      $shape      = array(
+        "SVGContours" => $glyph->getSVGContours(),
+        "xMin"        => $glyph->xMin,
+        "yMin"        => $glyph->yMin,
+        "xMax"        => $glyph->xMax,
+        "yMax"        => $glyph->yMax,
+      );
+      $shape_json = json_encode($shape);
+
+      $type = ($glyph instanceof OutlineSimple ? "simple" : "composite");
+      $char = isset($glyphIndexArray[$g]) ? $glyphIndexArray[$g] : 0;
+      $name = isset($names[$g]) ? $names[$g] : sprintf("uni%04x", $char);
+      $char = $char ? "&#{$glyphIndexArray[$g]};" : "";
+
+      $s .= "<div class='glyph-view $type' id='glyph-$g'>
+              <span class='glyph-id'>$g</span>
+              <span class='char'>$char</span>
+              <span class='char-name'>$name</span>
+              ";
+
+      if ($type == "composite") {
+        foreach ($glyph->getGlyphIDs() as $_id) {
+          $s .= "<a href='#glyph-$_id' class='glyph-component-id'>$_id</a> ";
+        }
+      }
+
+      $s .= "<br />
+            <canvas width='$width' height='$height' id='glyph-canvas-$g'></canvas>
+            </div>
+            <script>Glyph.glyphs.push([$g,$shape_json]);</script>";
+    }
+
+    return $s;
+  }
+
+
+  protected function _encode() {
+    $font   = $this->getFont();
+    $subset = $font->getSubset();
+    $data   = $this->data;
+
+    $loca = array();
+
+    $length = 0;
+    foreach ($subset as $gid) {
+      $loca[] = $length;
+      $length += $data[$gid]->encode();
+    }
+
+    $loca[]                             = $length; // dummy loca
+    $font->getTableObject("loca")->data = $loca;
+
+    return $length;
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: head.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,46 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+use Exception;
+
+/**
+ * `head` font table.
+ *
+ * @package php-font-lib
+ */
+class head extends Table {
+  protected $def = array(
+    "tableVersion"       => self::Fixed,
+    "fontRevision"       => self::Fixed,
+    "checkSumAdjustment" => self::uint32,
+    "magicNumber"        => self::uint32,
+    "flags"              => self::uint16,
+    "unitsPerEm"         => self::uint16,
+    "created"            => self::longDateTime,
+    "modified"           => self::longDateTime,
+    "xMin"               => self::FWord,
+    "yMin"               => self::FWord,
+    "xMax"               => self::FWord,
+    "yMax"               => self::FWord,
+    "macStyle"           => self::uint16,
+    "lowestRecPPEM"      => self::uint16,
+    "fontDirectionHint"  => self::int16,
+    "indexToLocFormat"   => self::int16,
+    "glyphDataFormat"    => self::int16,
+  );
+
+  protected function _parse() {
+    parent::_parse();
+
+    if ($this->data["magicNumber"] != 0x5F0F3CF5) {
+      throw new Exception("Incorrect magic number (" . dechex($this->data["magicNumber"]) . ")");
+    }
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: hhea.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,44 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `hhea` font table.
+ *
+ * @package php-font-lib
+ */
+class hhea extends Table {
+  protected $def = array(
+    "version"             => self::Fixed,
+    "ascent"              => self::FWord,
+    "descent"             => self::FWord,
+    "lineGap"             => self::FWord,
+    "advanceWidthMax"     => self::uFWord,
+    "minLeftSideBearing"  => self::FWord,
+    "minRightSideBearing" => self::FWord,
+    "xMaxExtent"          => self::FWord,
+    "caretSlopeRise"      => self::int16,
+    "caretSlopeRun"       => self::int16,
+    "caretOffset"         => self::FWord,
+    self::int16,
+    self::int16,
+    self::int16,
+    self::int16,
+    "metricDataFormat"    => self::int16,
+    "numOfLongHorMetrics" => self::uint16,
+  );
+
+  function _encode() {
+    $font                              = $this->getFont();
+    $this->data["numOfLongHorMetrics"] = count($font->getSubset());
+
+    return parent::_encode();
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: hmtx.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,59 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `hmtx` font table.
+ *
+ * @package php-font-lib
+ */
+class hmtx extends Table {
+  protected function _parse() {
+    $font   = $this->getFont();
+    $offset = $font->pos();
+
+    $numOfLongHorMetrics = $font->getData("hhea", "numOfLongHorMetrics");
+    $numGlyphs           = $font->getData("maxp", "numGlyphs");
+
+    $font->seek($offset);
+
+    $data = array();
+    $metrics = $font->readUInt16Many($numOfLongHorMetrics * 2);
+    for ($gid = 0, $mid = 0; $gid < $numOfLongHorMetrics; $gid++) {
+      $advanceWidth    = isset($metrics[$mid]) ? $metrics[$mid] : 0;
+      $mid += 1;
+      $leftSideBearing = isset($metrics[$mid]) ? $metrics[$mid] : 0;
+      $mid += 1;
+      $data[$gid]      = array($advanceWidth, $leftSideBearing);
+    }
+
+    if ($numOfLongHorMetrics < $numGlyphs) {
+      $lastWidth = end($data);
+      $data      = array_pad($data, $numGlyphs, $lastWidth);
+    }
+
+    $this->data = $data;
+  }
+
+  protected function _encode() {
+    $font   = $this->getFont();
+    $subset = $font->getSubset();
+    $data   = $this->data;
+
+    $length = 0;
+
+    foreach ($subset as $gid) {
+      $length += $font->writeUInt16($data[$gid][0]);
+      $length += $font->writeUInt16($data[$gid][1]);
+    }
+
+    return $length;
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: kern.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,80 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `kern` font table.
+ *
+ * @package php-font-lib
+ */
+class kern extends Table {
+  protected function _parse() {
+    $font = $this->getFont();
+
+    $data = $font->unpack(array(
+      "version"         => self::uint16,
+      "nTables"         => self::uint16,
+
+      // only the first subtable will be parsed
+      "subtableVersion" => self::uint16,
+      "length"          => self::uint16,
+      "coverage"        => self::uint16,
+    ));
+
+    $data["format"] = ($data["coverage"] >> 8);
+
+    $subtable = array();
+
+    switch ($data["format"]) {
+      case 0:
+        $subtable = $font->unpack(array(
+          "nPairs"        => self::uint16,
+          "searchRange"   => self::uint16,
+          "entrySelector" => self::uint16,
+          "rangeShift"    => self::uint16,
+        ));
+
+        $pairs = array();
+        $tree  = array();
+
+        $values = $font->readUInt16Many($subtable["nPairs"] * 3);
+        for ($i = 0, $idx = 0; $i < $subtable["nPairs"]; $i++) {
+          $left  = $values[$idx++];
+          $right = $values[$idx++];
+          $value = $values[$idx++];
+
+          if ($value >= 0x8000) {
+            $value -= 0x10000;
+          }
+
+          $pairs[] = array(
+            "left"  => $left,
+            "right" => $right,
+            "value" => $value,
+          );
+
+          $tree[$left][$right] = $value;
+        }
+
+        //$subtable["pairs"] = $pairs;
+        $subtable["tree"] = $tree;
+        break;
+
+      case 1:
+      case 2:
+      case 3:
+        break;
+    }
+
+    $data["subtable"] = $subtable;
+
+    $this->data = $data;
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: loca.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,80 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `loca` font table.
+ *
+ * @package php-font-lib
+ */
+class loca extends Table {
+  protected function _parse() {
+    $font   = $this->getFont();
+    $offset = $font->pos();
+
+    $indexToLocFormat = $font->getData("head", "indexToLocFormat");
+    $numGlyphs        = $font->getData("maxp", "numGlyphs");
+
+    $font->seek($offset);
+
+    $data = array();
+
+    // 2 bytes
+    if ($indexToLocFormat == 0) {
+      $d   = $font->read(($numGlyphs + 1) * 2);
+      $loc = unpack("n*", $d);
+
+      for ($i = 0; $i <= $numGlyphs; $i++) {
+        $data[] = isset($loc[$i + 1]) ?  $loc[$i + 1] * 2 : 0;
+      }
+    }
+
+    // 4 bytes
+    else {
+      if ($indexToLocFormat == 1) {
+        $d   = $font->read(($numGlyphs + 1) * 4);
+        $loc = unpack("N*", $d);
+
+        for ($i = 0; $i <= $numGlyphs; $i++) {
+          $data[] = isset($loc[$i + 1]) ? $loc[$i + 1] : 0;
+        }
+      }
+    }
+
+    $this->data = $data;
+  }
+
+  function _encode() {
+    $font = $this->getFont();
+    $data = $this->data;
+
+    $indexToLocFormat = $font->getData("head", "indexToLocFormat");
+    $numGlyphs        = $font->getData("maxp", "numGlyphs");
+    $length           = 0;
+
+    // 2 bytes
+    if ($indexToLocFormat == 0) {
+      for ($i = 0; $i <= $numGlyphs; $i++) {
+        $length += $font->writeUInt16($data[$i] / 2);
+      }
+    }
+
+    // 4 bytes
+    else {
+      if ($indexToLocFormat == 1) {
+        for ($i = 0; $i <= $numGlyphs; $i++) {
+          $length += $font->writeUInt32($data[$i]);
+        }
+      }
+    }
+
+    return $length;
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: maxp.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,42 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `maxp` font table.
+ *
+ * @package php-font-lib
+ */
+class maxp extends Table {
+  protected $def = array(
+    "version"               => self::Fixed,
+    "numGlyphs"             => self::uint16,
+    "maxPoints"             => self::uint16,
+    "maxContours"           => self::uint16,
+    "maxComponentPoints"    => self::uint16,
+    "maxComponentContours"  => self::uint16,
+    "maxZones"              => self::uint16,
+    "maxTwilightPoints"     => self::uint16,
+    "maxStorage"            => self::uint16,
+    "maxFunctionDefs"       => self::uint16,
+    "maxInstructionDefs"    => self::uint16,
+    "maxStackElements"      => self::uint16,
+    "maxSizeOfInstructions" => self::uint16,
+    "maxComponentElements"  => self::uint16,
+    "maxComponentDepth"     => self::uint16,
+  );
+
+  function _encode() {
+    $font                    = $this->getFont();
+    $this->data["numGlyphs"] = count($font->getSubset());
+
+    return parent::_encode();
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: name.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,193 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+
+use FontLib\Table\Table;
+use FontLib\Font;
+
+/**
+ * `name` font table.
+ *
+ * @package php-font-lib
+ */
+class name extends Table {
+  private static $header_format = array(
+    "format"       => self::uint16,
+    "count"        => self::uint16,
+    "stringOffset" => self::uint16,
+  );
+
+  const NAME_COPYRIGHT          = 0;
+  const NAME_NAME               = 1;
+  const NAME_SUBFAMILY          = 2;
+  const NAME_SUBFAMILY_ID       = 3;
+  const NAME_FULL_NAME          = 4;
+  const NAME_VERSION            = 5;
+  const NAME_POSTSCRIPT_NAME    = 6;
+  const NAME_TRADEMARK          = 7;
+  const NAME_MANUFACTURER       = 8;
+  const NAME_DESIGNER           = 9;
+  const NAME_DESCRIPTION        = 10;
+  const NAME_VENDOR_URL         = 11;
+  const NAME_DESIGNER_URL       = 12;
+  const NAME_LICENSE            = 13;
+  const NAME_LICENSE_URL        = 14;
+  const NAME_PREFERRE_FAMILY    = 16;
+  const NAME_PREFERRE_SUBFAMILY = 17;
+  const NAME_COMPAT_FULL_NAME   = 18;
+  const NAME_SAMPLE_TEXT        = 19;
+
+  static $nameIdCodes = array(
+    0  => "Copyright",
+    1  => "FontName",
+    2  => "FontSubfamily",
+    3  => "UniqueID",
+    4  => "FullName",
+    5  => "Version",
+    6  => "PostScriptName",
+    7  => "Trademark",
+    8  => "Manufacturer",
+    9  => "Designer",
+    10 => "Description",
+    11 => "FontVendorURL",
+    12 => "FontDesignerURL",
+    13 => "LicenseDescription",
+    14 => "LicenseURL",
+    // 15
+    16 => "PreferredFamily",
+    17 => "PreferredSubfamily",
+    18 => "CompatibleFullName",
+    19 => "SampleText",
+  );
+
+  static $platforms = array(
+    0 => "Unicode",
+    1 => "Macintosh",
+    // 2 =>  Reserved
+    3 => "Microsoft",
+  );
+
+  static $platformSpecific = array(
+    // Unicode
+    0 => array(
+      0 => "Default semantics",
+      1 => "Version 1.1 semantics",
+      2 => "ISO 10646 1993 semantics (deprecated)",
+      3 => "Unicode 2.0 or later semantics",
+    ),
+
+    // Macintosh
+    1 => array(
+      0  => "Roman",
+      1  => "Japanese",
+      2  => "Traditional Chinese",
+      3  => "Korean",
+      4  => "Arabic",
+      5  => "Hebrew",
+      6  => "Greek",
+      7  => "Russian",
+      8  => "RSymbol",
+      9  => "Devanagari",
+      10 => "Gurmukhi",
+      11 => "Gujarati",
+      12 => "Oriya",
+      13 => "Bengali",
+      14 => "Tamil",
+      15 => "Telugu",
+      16 => "Kannada",
+      17 => "Malayalam",
+      18 => "Sinhalese",
+      19 => "Burmese",
+      20 => "Khmer",
+      21 => "Thai",
+      22 => "Laotian",
+      23 => "Georgian",
+      24 => "Armenian",
+      25 => "Simplified Chinese",
+      26 => "Tibetan",
+      27 => "Mongolian",
+      28 => "Geez",
+      29 => "Slavic",
+      30 => "Vietnamese",
+      31 => "Sindhi",
+    ),
+
+    // Microsoft
+    3 => array(
+      0  => "Symbol",
+      1  => "Unicode BMP (UCS-2)",
+      2  => "ShiftJIS",
+      3  => "PRC",
+      4  => "Big5",
+      5  => "Wansung",
+      6  => "Johab",
+      //  7 => Reserved
+      //  8 => Reserved
+      //  9 => Reserved
+      10 => "Unicode UCS-4",
+    ),
+  );
+
+  protected function _parse() {
+    $font = $this->getFont();
+
+    $tableOffset = $font->pos();
+
+    $data = $font->unpack(self::$header_format);
+
+    $records = array();
+    for ($i = 0; $i < $data["count"]; $i++) {
+      $record      = new nameRecord();
+      $record_data = $font->unpack(nameRecord::$format);
+      $record->map($record_data);
+
+      $records[] = $record;
+    }
+
+    $names = array();
+    foreach ($records as $record) {
+      $font->seek($tableOffset + $data["stringOffset"] + $record->offset);
+      $s                      = $font->read($record->length);
+      $record->string         = Font::UTF16ToUTF8($s);
+      $names[$record->nameID] = $record;
+    }
+
+    $data["records"] = $names;
+
+    $this->data = $data;
+  }
+
+  protected function _encode() {
+    $font = $this->getFont();
+
+    /** @var nameRecord[] $records */
+    $records       = $this->data["records"];
+    $count_records = count($records);
+
+    $this->data["count"]        = $count_records;
+    $this->data["stringOffset"] = 6 + $count_records * 12; // 6 => uint16 * 3, 12 => sizeof self::$record_format
+
+    $length = $font->pack(self::$header_format, $this->data);
+
+    $offset = 0;
+    foreach ($records as $record) {
+      $record->length = mb_strlen($record->getUTF16(), "8bit");
+      $record->offset = $offset;
+      $offset += $record->length;
+      $length += $font->pack(nameRecord::$format, (array)$record);
+    }
+
+    foreach ($records as $record) {
+      $str = $record->getUTF16();
+      $length += $font->write($str, mb_strlen($str, "8bit"));
+    }
+
+    return $length;
+  }
+}

programming_language: PHP
filename: nameRecord.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,53 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+namespace FontLib\Table\Type;
+
+use FontLib\Font;
+use FontLib\BinaryStream;
+
+/**
+ * Font table name record.
+ *
+ * @package php-font-lib
+ */
+class nameRecord extends BinaryStream {
+  public $platformID;
+  public $platformSpecificID;
+  public $languageID;
+  public $nameID;
+  public $length;
+  public $offset;
+  public $string;
+
+  public static $format = array(
+    "platformID"         => self::uint16,
+    "platformSpecificID" => self::uint16,
+    "languageID"         => self::uint16,
+    "nameID"             => self::uint16,
+    "length"             => self::uint16,
+    "offset"             => self::uint16,
+  );
+
+  public function map($data) {
+    foreach ($data as $key => $value) {
+      $this->$key = $value;
+    }
+  }
+
+  public function getUTF8() {
+    return $this->string;
+  }
+
+  public function getUTF16() {
+    return Font::UTF8ToUTF16($this->string);
+  }
+
+  function __toString() {
+    return $this->string;
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: os2.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,47 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+
+/**
+ * `OS/2` font table.
+ *
+ * @package php-font-lib
+ */
+class os2 extends Table {
+  protected $def = array(
+    "version"             => self::uint16,
+    "xAvgCharWidth"       => self::int16,
+    "usWeightClass"       => self::uint16,
+    "usWidthClass"        => self::uint16,
+    "fsType"              => self::int16,
+    "ySubscriptXSize"     => self::int16,
+    "ySubscriptYSize"     => self::int16,
+    "ySubscriptXOffset"   => self::int16,
+    "ySubscriptYOffset"   => self::int16,
+    "ySuperscriptXSize"   => self::int16,
+    "ySuperscriptYSize"   => self::int16,
+    "ySuperscriptXOffset" => self::int16,
+    "ySuperscriptYOffset" => self::int16,
+    "yStrikeoutSize"      => self::int16,
+    "yStrikeoutPosition"  => self::int16,
+    "sFamilyClass"        => self::int16,
+    "panose"              => array(self::uint8, 10),
+    "ulCharRange"         => array(self::uint32, 4),
+    "achVendID"           => array(self::char, 4),
+    "fsSelection"         => self::uint16,
+    "fsFirstCharIndex"    => self::uint16,
+    "fsLastCharIndex"     => self::uint16,
+    "typoAscender"        => self::int16,
+    "typoDescender"       => self::int16,
+    "typoLineGap"         => self::int16,
+    "winAscent"           => self::int16,
+    "winDescent"          => self::int16,
+  );
+}
\ No newline at end of file

programming_language: PHP
filename: post.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,143 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\Table\Type;
+use FontLib\Table\Table;
+use FontLib\TrueType\File;
+
+/**
+ * `post` font table.
+ *
+ * @package php-font-lib
+ */
+class post extends Table {
+  protected $def = array(
+    "format"             => self::Fixed,
+    "italicAngle"        => self::Fixed,
+    "underlinePosition"  => self::FWord,
+    "underlineThickness" => self::FWord,
+    "isFixedPitch"       => self::uint32,
+    "minMemType42"       => self::uint32,
+    "maxMemType42"       => self::uint32,
+    "minMemType1"        => self::uint32,
+    "maxMemType1"        => self::uint32,
+  );
+
+  protected function _parse() {
+    $font = $this->getFont();
+    $data = $font->unpack($this->def);
+
+    $names = array();
+
+    switch ($data["format"]) {
+      case 1:
+        $names = File::$macCharNames;
+        break;
+
+      case 2:
+        $data["numberOfGlyphs"] = $font->readUInt16();
+
+        $glyphNameIndex = $font->readUInt16Many($data["numberOfGlyphs"]);
+
+        $data["glyphNameIndex"] = $glyphNameIndex;
+
+        $namesPascal = array();
+        for ($i = 0; $i < $data["numberOfGlyphs"]; $i++) {
+          $len           = $font->readUInt8();
+          $namesPascal[] = $font->read($len);
+        }
+
+        foreach ($glyphNameIndex as $g => $index) {
+          if ($index < 258) {
+            $names[$g] = File::$macCharNames[$index];
+          }
+          else {
+            if (array_key_exists($index - 258, $namesPascal)) {
+              $names[$g] = $namesPascal[$index - 258];
+            }
+          }
+        }
+
+        break;
+
+      case 2.5:
+        // TODO
+        break;
+
+      case 3:
+        // nothing
+        break;
+
+      case 4:
+        // TODO
+        break;
+    }
+
+    $data["names"] = $names;
+
+    $this->data = $data;
+  }
+
+  function _encode() {
+    $font           = $this->getFont();
+    $data           = $this->data;
+    $data["format"] = 3;
+
+    $length = $font->pack($this->def, $data);
+
+    return $length;
+    /*
+    $subset = $font->getSubset();
+
+    switch($data["format"]) {
+      case 1:
+        // nothing to do
+      break;
+
+      case 2:
+        $old_names = $data["names"];
+
+        $glyphNameIndex = range(0, count($subset));
+
+        $names = array();
+        foreach($subset as $gid) {
+          $names[] = $data["names"][$data["glyphNameIndex"][$gid]];
+        }
+
+        $numberOfGlyphs = count($names);
+        $length += $font->writeUInt16($numberOfGlyphs);
+
+        foreach($glyphNameIndex as $gni) {
+          $length += $font->writeUInt16($gni);
+        }
+
+        //$names = array_slice($names, 257);
+        foreach($names as $name) {
+          $len = strlen($name);
+          $length += $font->writeUInt8($len);
+          $length += $font->write($name, $len);
+        }
+
+      break;
+
+      case 2.5:
+        // TODO
+      break;
+
+      case 3:
+        // nothing
+      break;
+
+      case 4:
+        // TODO
+      break;
+    }
+
+    return $length;*/
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: Collection.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,100 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\TrueType;
+
+use Countable;
+use FontLib\BinaryStream;
+use Iterator;
+use OutOfBoundsException;
+
+/**
+ * TrueType collection font file.
+ *
+ * @package php-font-lib
+ */
+class Collection extends BinaryStream implements Iterator, Countable {
+  /**
+   * Current iterator position.
+   *
+   * @var integer
+   */
+  private $position = 0;
+
+  protected $collectionOffsets = array();
+  protected $collection = array();
+  protected $version;
+  protected $numFonts;
+
+  function parse() {
+    if (isset($this->numFonts)) {
+      return;
+    }
+
+    $this->read(4); // tag name
+
+    $this->version  = $this->readFixed();
+    $this->numFonts = $this->readUInt32();
+
+    for ($i = 0; $i < $this->numFonts; $i++) {
+      $this->collectionOffsets[] = $this->readUInt32();
+    }
+  }
+
+  /**
+   * @param int $fontId
+   *
+   * @throws OutOfBoundsException
+   * @return File
+   */
+  function getFont($fontId) {
+    $this->parse();
+
+    if (!isset($this->collectionOffsets[$fontId])) {
+      throw new OutOfBoundsException();
+    }
+
+    if (isset($this->collection[$fontId])) {
+      return $this->collection[$fontId];
+    }
+
+    $font    = new File();
+    $font->f = $this->f;
+    $font->setTableOffset($this->collectionOffsets[$fontId]);
+
+    return $this->collection[$fontId] = $font;
+  }
+
+  function current() {
+    return $this->getFont($this->position);
+  }
+
+  function key() {
+    return $this->position;
+  }
+
+  function next() {
+    return ++$this->position;
+  }
+
+  function rewind() {
+    $this->position = 0;
+  }
+
+  function valid() {
+    $this->parse();
+
+    return isset($this->collectionOffsets[$this->position]);
+  }
+
+  function count() {
+    $this->parse();
+
+    return $this->numFonts;
+  }
+}

programming_language: PHP
filename: File.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,471 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\TrueType;
+
+use FontLib\AdobeFontMetrics;
+use FontLib\Font;
+use FontLib\BinaryStream;
+use FontLib\Table\Table;
+use FontLib\Table\DirectoryEntry;
+use FontLib\Table\Type\glyf;
+use FontLib\Table\Type\name;
+use FontLib\Table\Type\nameRecord;
+
+/**
+ * TrueType font file.
+ *
+ * @package php-font-lib
+ */
+class File extends BinaryStream {
+  /**
+   * @var Header
+   */
+  public $header = array();
+
+  private $tableOffset = 0; // Used for TTC
+
+  private static $raw = false;
+
+  protected $directory = array();
+  protected $data = array();
+
+  protected $glyph_subset = array();
+
+  public $glyph_all = array();
+
+  static $macCharNames = array(
+    ".notdef", ".null", "CR",
+    "space", "exclam", "quotedbl", "numbersign",
+    "dollar", "percent", "ampersand", "quotesingle",
+    "parenleft", "parenright", "asterisk", "plus",
+    "comma", "hyphen", "period", "slash",
+    "zero", "one", "two", "three",
+    "four", "five", "six", "seven",
+    "eight", "nine", "colon", "semicolon",
+    "less", "equal", "greater", "question",
+    "at", "A", "B", "C", "D", "E", "F", "G",
+    "H", "I", "J", "K", "L", "M", "N", "O",
+    "P", "Q", "R", "S", "T", "U", "V", "W",
+    "X", "Y", "Z", "bracketleft",
+    "backslash", "bracketright", "asciicircum", "underscore",
+    "grave", "a", "b", "c", "d", "e", "f", "g",
+    "h", "i", "j", "k", "l", "m", "n", "o",
+    "p", "q", "r", "s", "t", "u", "v", "w",
+    "x", "y", "z", "braceleft",
+    "bar", "braceright", "asciitilde", "Adieresis",
+    "Aring", "Ccedilla", "Eacute", "Ntilde",
+    "Odieresis", "Udieresis", "aacute", "agrave",
+    "acircumflex", "adieresis", "atilde", "aring",
+    "ccedilla", "eacute", "egrave", "ecircumflex",
+    "edieresis", "iacute", "igrave", "icircumflex",
+    "idieresis", "ntilde", "oacute", "ograve",
+    "ocircumflex", "odieresis", "otilde", "uacute",
+    "ugrave", "ucircumflex", "udieresis", "dagger",
+    "degree", "cent", "sterling", "section",
+    "bullet", "paragraph", "germandbls", "registered",
+    "copyright", "trademark", "acute", "dieresis",
+    "notequal", "AE", "Oslash", "infinity",
+    "plusminus", "lessequal", "greaterequal", "yen",
+    "mu", "partialdiff", "summation", "product",
+    "pi", "integral", "ordfeminine", "ordmasculine",
+    "Omega", "ae", "oslash", "questiondown",
+    "exclamdown", "logicalnot", "radical", "florin",
+    "approxequal", "increment", "guillemotleft", "guillemotright",
+    "ellipsis", "nbspace", "Agrave", "Atilde",
+    "Otilde", "OE", "oe", "endash",
+    "emdash", "quotedblleft", "quotedblright", "quoteleft",
+    "quoteright", "divide", "lozenge", "ydieresis",
+    "Ydieresis", "fraction", "currency", "guilsinglleft",
+    "guilsinglright", "fi", "fl", "daggerdbl",
+    "periodcentered", "quotesinglbase", "quotedblbase", "perthousand",
+    "Acircumflex", "Ecircumflex", "Aacute", "Edieresis",
+    "Egrave", "Iacute", "Icircumflex", "Idieresis",
+    "Igrave", "Oacute", "Ocircumflex", "applelogo",
+    "Ograve", "Uacute", "Ucircumflex", "Ugrave",
+    "dotlessi", "circumflex", "tilde", "macron",
+    "breve", "dotaccent", "ring", "cedilla",
+    "hungarumlaut", "ogonek", "caron", "Lslash",
+    "lslash", "Scaron", "scaron", "Zcaron",
+    "zcaron", "brokenbar", "Eth", "eth",
+    "Yacute", "yacute", "Thorn", "thorn",
+    "minus", "multiply", "onesuperior", "twosuperior",
+    "threesuperior", "onehalf", "onequarter", "threequarters",
+    "franc", "Gbreve", "gbreve", "Idot",
+    "Scedilla", "scedilla", "Cacute", "cacute",
+    "Ccaron", "ccaron", "dmacron"
+  );
+
+  function getTable() {
+    $this->parseTableEntries();
+
+    return $this->directory;
+  }
+
+  function setTableOffset($offset) {
+    $this->tableOffset = $offset;
+  }
+
+  function parse() {
+    $this->parseTableEntries();
+
+    $this->data = array();
+
+    foreach ($this->directory as $tag => $table) {
+      if (empty($this->data[$tag])) {
+        $this->readTable($tag);
+      }
+    }
+  }
+
+  function utf8toUnicode($str) {
+    $len = mb_strlen($str, '8bit');
+    $out = array();
+
+    for ($i = 0; $i < $len; $i++) {
+      $uni = -1;
+      $h   = ord($str[$i]);
+
+      if ($h <= 0x7F) {
+        $uni = $h;
+      }
+      elseif ($h >= 0xC2) {
+        if (($h <= 0xDF) && ($i < $len - 1)) {
+          $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
+        }
+        elseif (($h <= 0xEF) && ($i < $len - 2)) {
+          $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
+        }
+        elseif (($h <= 0xF4) && ($i < $len - 3)) {
+          $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
+        }
+      }
+
+      if ($uni >= 0) {
+        $out[] = $uni;
+      }
+    }
+
+    return $out;
+  }
+
+  function getUnicodeCharMap() {
+    $subtable = null;
+    foreach ($this->getData("cmap", "subtables") as $_subtable) {
+      if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
+        $subtable = $_subtable;
+        break;
+      }
+    }
+
+    if ($subtable) {
+      return $subtable["glyphIndexArray"];
+    }
+
+    return null;
+  }
+
+  function setSubset($subset) {
+    if (!is_array($subset)) {
+      $subset = $this->utf8toUnicode($subset);
+    }
+
+    $subset = array_unique($subset);
+
+    $glyphIndexArray = $this->getUnicodeCharMap();
+
+    if (!$glyphIndexArray) {
+      return;
+    }
+
+    $gids = array(
+      0, // .notdef
+      1, // .null
+    );
+
+    foreach ($subset as $code) {
+      if (!isset($glyphIndexArray[$code])) {
+        continue;
+      }
+
+      $gid        = $glyphIndexArray[$code];
+      $gids[$gid] = $gid;
+    }
+
+    /** @var glyf $glyf */
+    $glyf = $this->getTableObject("glyf");
+    $gids = $glyf->getGlyphIDs($gids);
+
+    sort($gids);
+
+    $this->glyph_subset = $gids;
+    $this->glyph_all    = array_values($glyphIndexArray); // FIXME
+  }
+
+  function getSubset() {
+    if (empty($this->glyph_subset)) {
+      return $this->glyph_all;
+    }
+
+    return $this->glyph_subset;
+  }
+
+  function encode($tags = array()) {
+    if (!self::$raw) {
+      $tags = array_merge(array("head", "hhea", "cmap", "hmtx", "maxp", "glyf", "loca", "name", "post"), $tags);
+    }
+    else {
+      $tags = array_keys($this->directory);
+    }
+
+    $num_tables = count($tags);
+    $n          = 16; // @todo
+
+    Font::d("Tables : " . implode(", ", $tags));
+
+    /** @var DirectoryEntry[] $entries */
+    $entries = array();
+    foreach ($tags as $tag) {
+      if (!isset($this->directory[$tag])) {
+        Font::d("  >> '$tag' table doesn't exist");
+        continue;
+      }
+
+      $entries[$tag] = $this->directory[$tag];
+    }
+
+    $this->header->data["numTables"] = $num_tables;
+    $this->header->encode();
+
+    $directory_offset = $this->pos();
+    $offset           = $directory_offset + $num_tables * $n;
+    $this->seek($offset);
+
+    $i = 0;
+    foreach ($entries as $entry) {
+      $entry->encode($directory_offset + $i * $n);
+      $i++;
+    }
+  }
+
+  function parseHeader() {
+    if (!empty($this->header)) {
+      return;
+    }
+
+    $this->seek($this->tableOffset);
+
+    $this->header = new Header($this);
+    $this->header->parse();
+  }
+
+  function getFontType(){
+    $class_parts = explode("\\", get_class($this));
+    return $class_parts[1];
+  }
+
+  function parseTableEntries() {
+    $this->parseHeader();
+
+    if (!empty($this->directory)) {
+      return;
+    }
+
+    if (empty($this->header->data["numTables"])) {
+      return;
+    }
+
+
+    $type = $this->getFontType();
+    $class = "FontLib\\$type\\TableDirectoryEntry";
+
+    for ($i = 0; $i < $this->header->data["numTables"]; $i++) {
+      /** @var TableDirectoryEntry $entry */
+      $entry = new $class($this);
+      $entry->parse();
+
+      $this->directory[$entry->tag] = $entry;
+    }
+  }
+
+  function normalizeFUnit($value, $base = 1000) {
+    return round($value * ($base / $this->getData("head", "unitsPerEm")));
+  }
+
+  protected function readTable($tag) {
+    $this->parseTableEntries();
+
+    if (!self::$raw) {
+      $name_canon = preg_replace("/[^a-z0-9]/", "", strtolower($tag));
+
+      $class = "FontLib\\Table\\Type\\$name_canon";
+
+      if (!isset($this->directory[$tag]) || !@class_exists($class)) {
+        return;
+      }
+    }
+    else {
+      $class = "FontLib\\Table\\Table";
+    }
+
+    /** @var Table $table */
+    $table = new $class($this->directory[$tag]);
+    $table->parse();
+
+    $this->data[$tag] = $table;
+  }
+
+  /**
+   * @param $name
+   *
+   * @return Table
+   */
+  public function getTableObject($name) {
+    return $this->data[$name];
+  }
+
+  public function setTableObject($name, Table $data) {
+    $this->data[$name] = $data;
+  }
+
+  public function getData($name, $key = null) {
+    $this->parseTableEntries();
+
+    if (empty($this->data[$name])) {
+      $this->readTable($name);
+    }
+
+    if (!isset($this->data[$name])) {
+      return null;
+    }
+
+    if (!$key) {
+      return $this->data[$name]->data;
+    }
+    else {
+      return $this->data[$name]->data[$key];
+    }
+  }
+
+  function addDirectoryEntry(DirectoryEntry $entry) {
+    $this->directory[$entry->tag] = $entry;
+  }
+
+  function saveAdobeFontMetrics($file, $encoding = null) {
+    $afm = new AdobeFontMetrics($this);
+    $afm->write($file, $encoding);
+  }
+
+  /**
+   * Get a specific name table string value from its ID
+   *
+   * @param int $nameID The name ID
+   *
+   * @return string|null
+   */
+  function getNameTableString($nameID) {
+    /** @var nameRecord[] $records */
+    $records = $this->getData("name", "records");
+
+    if (!isset($records[$nameID])) {
+      return null;
+    }
+
+    return $records[$nameID]->string;
+  }
+
+  /**
+   * Get font copyright
+   *
+   * @return string|null
+   */
+  function getFontCopyright() {
+    return $this->getNameTableString(name::NAME_COPYRIGHT);
+  }
+
+  /**
+   * Get font name
+   *
+   * @return string|null
+   */
+  function getFontName() {
+    return $this->getNameTableString(name::NAME_NAME);
+  }
+
+  /**
+   * Get font subfamily
+   *
+   * @return string|null
+   */
+  function getFontSubfamily() {
+    return $this->getNameTableString(name::NAME_SUBFAMILY);
+  }
+
+  /**
+   * Get font subfamily ID
+   *
+   * @return string|null
+   */
+  function getFontSubfamilyID() {
+    return $this->getNameTableString(name::NAME_SUBFAMILY_ID);
+  }
+
+  /**
+   * Get font full name
+   *
+   * @return string|null
+   */
+  function getFontFullName() {
+    return $this->getNameTableString(name::NAME_FULL_NAME);
+  }
+
+  /**
+   * Get font version
+   *
+   * @return string|null
+   */
+  function getFontVersion() {
+    return $this->getNameTableString(name::NAME_VERSION);
+  }
+
+  /**
+   * Get font weight
+   *
+   * @return string|null
+   */
+  function getFontWeight() {
+    return $this->getTableObject("OS/2")->data["usWeightClass"];
+  }
+
+  /**
+   * Get font Postscript name
+   *
+   * @return string|null
+   */
+  function getFontPostscriptName() {
+    return $this->getNameTableString(name::NAME_POSTSCRIPT_NAME);
+  }
+
+  function reduce() {
+    $names_to_keep = array(
+      name::NAME_COPYRIGHT,
+      name::NAME_NAME,
+      name::NAME_SUBFAMILY,
+      name::NAME_SUBFAMILY_ID,
+      name::NAME_FULL_NAME,
+      name::NAME_VERSION,
+      name::NAME_POSTSCRIPT_NAME,
+    );
+
+    foreach ($this->data["name"]->data["records"] as $id => $rec) {
+      if (!in_array($id, $names_to_keep)) {
+        unset($this->data["name"]->data["records"][$id]);
+      }
+    }
+  }
+}

programming_language: PHP
filename: Header.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,31 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\TrueType;
+
+/**
+ * TrueType font file header.
+ *
+ * @package php-font-lib
+ */
+class Header extends \FontLib\Header {
+  protected $def = array(
+    "format"        => self::uint32,
+    "numTables"     => self::uint16,
+    "searchRange"   => self::uint16,
+    "entrySelector" => self::uint16,
+    "rangeShift"    => self::uint16,
+  );
+
+  public function parse() {
+    parent::parse();
+
+    $format                   = $this->data["format"];
+    $this->data["formatText"] = $this->convertUInt32ToStr($format);
+  }
+}
\ No newline at end of file

programming_language: PHP
filename: TableDirectoryEntry.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,33 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\TrueType;
+
+use FontLib\Table\DirectoryEntry;
+
+/**
+ * TrueType table directory entry.
+ *
+ * @package php-font-lib
+ */
+class TableDirectoryEntry extends DirectoryEntry {
+  function __construct(File $font) {
+    parent::__construct($font);
+  }
+
+  function parse() {
+    parent::parse();
+
+    $font           = $this->font;
+    $this->checksum = $font->readUInt32();
+    $this->offset   = $font->readUInt32();
+    $this->length   = $font->readUInt32();
+    $this->entryLength += 12;
+  }
+}
+

programming_language: PHP
filename: File.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,81 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\WOFF;
+
+use FontLib\Table\DirectoryEntry;
+
+/**
+ * WOFF font file.
+ *
+ * @package php-font-lib
+ *
+ * @property TableDirectoryEntry[] $directory
+ */
+class File extends \FontLib\TrueType\File {
+  function parseHeader() {
+    if (!empty($this->header)) {
+      return;
+    }
+
+    $this->header = new Header($this);
+    $this->header->parse();
+  }
+
+  public function load($file) {
+    parent::load($file);
+
+    $this->parseTableEntries();
+    $dataOffset = $this->pos() + count($this->directory) * 20;
+
+    $fw = $this->getTempFile(false);
+    $fr = $this->f;
+
+    $this->f = $fw;
+    $offset  = $this->header->encode();
+
+    foreach ($this->directory as $entry) {
+      // Read ...
+      $this->f = $fr;
+      $this->seek($entry->offset);
+      $data = $this->read($entry->length);
+
+      if ($entry->length < $entry->origLength) {
+        $data = (string) gzuncompress($data);
+      }
+
+      // Prepare data ...
+      $length        = mb_strlen($data, '8bit');
+      $entry->length = $entry->origLength = $length;
+      $entry->offset = $dataOffset;
+
+      // Write ...
+      $this->f = $fw;
+
+      // Woff Entry
+      $this->seek($offset);
+      $offset += $this->write($entry->tag, 4); // tag
+      $offset += $this->writeUInt32($dataOffset); // offset
+      $offset += $this->writeUInt32($length); // length
+      $offset += $this->writeUInt32($length); // origLength
+      $offset += $this->writeUInt32(DirectoryEntry::computeChecksum($data)); // checksum
+
+      // Data
+      $this->seek($dataOffset);
+      $dataOffset += $this->write($data, $length);
+    }
+
+    $this->f = $fw;
+    $this->seek(0);
+
+    // Need to re-parse this, don't know why
+    $this->header    = null;
+    $this->directory = array();
+    $this->parseTableEntries();
+  }
+}

programming_language: PHP
filename: Header.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,32 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\WOFF;
+
+/**
+ * WOFF font file header.
+ *
+ * @package php-font-lib
+ */
+class Header extends \FontLib\TrueType\Header {
+  protected $def = array(
+    "format"         => self::uint32,
+    "flavor"         => self::uint32,
+    "length"         => self::uint32,
+    "numTables"      => self::uint16,
+    self::uint16,
+    "totalSfntSize"  => self::uint32,
+    "majorVersion"   => self::uint16,
+    "minorVersion"   => self::uint16,
+    "metaOffset"     => self::uint32,
+    "metaLength"     => self::uint32,
+    "metaOrigLength" => self::uint32,
+    "privOffset"     => self::uint32,
+    "privLength"     => self::uint32,
+  );
+}
\ No newline at end of file

programming_language: PHP
filename: TableDirectoryEntry.php
change_type: ModificationType.ADD
diff: @@ -0,0 +1,34 @@
+<?php
+/**
+ * @package php-font-lib
+ * @link    https://github.com/PhenX/php-font-lib
+ * @author  Fabien Ménager <fabien.menager@gmail.com>
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ */
+
+namespace FontLib\WOFF;
+
+use FontLib\Table\DirectoryEntry;
+
+/**
+ * WOFF font file table directory entry.
+ *
+ * @package php-font-lib
+ */
+class TableDirectoryEntry extends DirectoryEntry {
+  public $origLength;
+
+  function __construct(File $font) {
+    parent::__construct($font);
+  }
+
+  function parse() {
+    parent::parse();
+
+    $font             = $this->font;
+    $this->offset     = $font->readUInt32();
+    $this->length     = $font->readUInt32();
+    $this->origLength = $font->readUInt32();
+    $this->checksum   = $font->readUInt32();
+  }
+}

programming_language: PHP
filename: processNotificationBatchDetails.php
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
@@ -15,133 +15,106 @@
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
-	//include the dompdf class
-	require_once("dompdf/dompdf_config.inc.php");
 
-	//include the Pear Mail classes for sending out emails
-	@require_once('Mail.php');
-	@require_once('Mail/mime.php');
-	
-	$base = dirname(__FILE__);
-	
-	/**
-	 * createBatchDetailsNotification()
-	 * wrapper-function to create the notification to the customer
-	 * 
-	 * @param		array			customer information array
-	 */
-	function createBatchDetailsNotification($customerInfo) {
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationBatchDetails.php') !== false) {
+        header("Location: ../index.php");
+        exit;
+    }
 
-		global $base;
-		
-		$html = prepareNotificationTemplate($customerInfo);
-		$pdfDocument = createPDF($html);
-		
-		return $pdfDocument;
-		
-	}
+    // common notification functions
+    include("common.php");
+    
+    $base = dirname(__FILE__);
+    $base_path = "$base/templates/";
+    
+    /**
+     * createBatchDetailsNotification()
+     * wrapper-function to create the notification to the customer
+     * 
+     * @param        array        customer information array
+     */
+    function createBatchDetailsNotification($customerInfo) {
+        global $base_path;
+        
+        $html = prepareNotificationTemplate($customerInfo);
+        return createPDF($html, $base_path);
+    }
 
-	
-	/**
-	 * emailNotification()
-	 * creates an email message with the pdf and sends it
-	 * 
-	 * @param		string			pdf binary/string stream
-	 * @param		array			customer information array
-	 * @param		array			smtp server information
-	 * @param		string			from email address of the sender identity
-	 */
-	function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
+    
+    /**
+     * emailNotification()
+     * creates an email message with the pdf and sends it
+     * 
+     * @param        string        pdf binary/string stream
+     * @param        array         customer information array
+     * @param        array         smtp server information
+     * @param        string        from email address of the sender identity
+     */
+    function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
+        $sendTo = $customerInfo['business_email'];
+        $subject = "Business Pre-Paid Batch Information";
+        $body = "Notification letter of service";
+        $attachmentName = "notification.pdf";
+        
+        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
+    }
+    
+    
+    /**
+     * prepareNotificationTemplate()
+     * reads the notification (html) template and returns the formatted (html) notification
+     * 
+     * @param         array         Array of notification information
+     * @return        string        HTML notification
+     */
+    function prepareNotificationTemplate($customerInfo) {
+        global $base_path;
+        
+        // the HTML template
+        $notification_template = $base_path . "/batch_details.html";
+        
+        $notification_html_template = file_get_contents($notification_template);
+        
+        $date = date("Y-m-d");
+        
+        $business_name = $customerInfo['business_name'];
+        $business_owner_name = $customerInfo['business_owner_name']; 
+        $business_address = $customerInfo['business_address'];
+        $business_phone = $customerInfo['business_phone'];
+        $business_email = $customerInfo['business_email'];
+        $business_web = $customerInfo['business_web'];
+        $batch_details = $customerInfo['batch_details'];
+        $batch_active_users = $customerInfo['batch_active_users'];
+        $service_plan_info = $customerInfo['service_plan_info'];
+    
+        $tagname_value_mappings = array(
+                                            "__INVOICE_CREATION_DATE__" => $date,
+                                            "__BUSINESS_NAME__" => $business_name,
+                                            "__BUSINESS_OWNER_NAME__" => $business_owner_name,
+                                            "__BUSINESS_ADDRESS__" => $business_address,
+                                            "__BUSINESS_PHONE__" => $business_phone,
+                                            "__BUSINESS_EMAIL__" => $business_email,
+                                            "__BUSINESS_WEB__" => $business_web,
+                                            "__BATCH_DETAILS__" => $batch_details,
+                                            "__BATCH_ACTIVE_USERS__" => $batch_active_users,
+                                            "__SERVICE_PLAN_INFO__" => $service_plan_info,
+                                       );
+    
+        foreach ($tagname_value_mappings as $tagname => $value) {
+            $tag = sprintf("####%s####", $tagname);
+            $notification_html_template = str_replace($tag, $value, $notification_html_template);
+        }
 
-		global $base;
-		
-		if (empty($customerInfo['business_email']))
-			return;
-		
-		$headers = array(	"From"	=>	$from, 
-							"Subject"	=>	"Business Pre-Paid Batch Information",
-							"Reply-To"=> $from
-					);
-				
-		$mime = new Mail_mime();
-		$mime->setTXTBody("Notification letter of service"); 
-		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
-		$body = $mime->get();
-		$headers = $mime->headers($headers);
-		$mail =& Mail::factory("smtp", $smtpInfo);
-		$mail->send($customerInfo['business_email'], $headers, $body);
-	
-	}
-	
-	
-	/**
-	 * prepareNotificationTemplate()
-	 * reads the notification (html) template and returns the formatted (html) notification
-	 * 
-	 * @param		array			Array of notification information
-	 * @return		string			HTML notification
-	 */
-	function prepareNotificationTemplate($customerInfo) {
-	
-		global $base;
-		
-		// the HTML template
-		$notification_template = "$base/templates/batch_details.html";
-		$notification_html_template = file_get_contents($notification_template);
-	
-		$date = date("Y-m-d");
-		
-		$business_name = $customerInfo['business_name'];
-		$business_owner_name = $customerInfo['business_owner_name']; 
-		$business_address = $customerInfo['business_address'];
-		$business_phone = $customerInfo['business_phone'];
-		$business_email = $customerInfo['business_email'];
-		$business_web = $customerInfo['business_web'];
-		$batch_details = $customerInfo['batch_details'];
-		$batch_active_users = $customerInfo['batch_active_users'];
-		$service_plan_info = $customerInfo['service_plan_info'];
-	
-		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
-		
-		$notification_html_template = str_replace("####__BUSINESS_NAME__####", $business_name, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_OWNER_NAME__####", $business_owner_name, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_ADDRESS__####", $business_address, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_PHONE__####", $business_phone, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_EMAIL__####", $business_email, $notification_html_template);
-		$notification_html_template = str_replace("####__BUSINESS_WEB__####", $business_web, $notification_html_template);
-		$notification_html_template = str_replace("####__BATCH_DETAILS__####", $batch_details, $notification_html_template);
-		$notification_html_template = str_replace("####__BATCH_ACTIVE_USERS__####", $batch_active_users, $notification_html_template);
-		$notification_html_template = str_replace("####__SERVICE_PLAN_INFO__####", $service_plan_info, $notification_html_template);
-		
+        // this fix has been set in place according to this thread:
+        // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
+        return str_replace("\n", "", $notification_html_template);
+    }
 
-		return $notification_html_template;
-	}
-	
-	/**
-	 * createPDF()
-	 * creates a PDF document for a given html file
-	 * 
-	 * @param 		string			the html file to convert to pdf			
-	 * @return 		string			returns the pdf in binary/string stream
-	 */
-	function createPDF($html) {
-	
-		global $base;
-		
-		// instansiate the pdf document
-		$dompdf = new DOMPDF();
-		$dompdf->set_base_path("$base/templates/");
-		$dompdf->load_html($html);
-		$dompdf->render();
-	
-		$notification_pdf = $dompdf->output();
-		
-		return $notification_pdf;
-
-	}
 
 ?>

programming_language: PHP
filename: processNotificationUserDetailsInvoice.php
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
@@ -15,19 +15,24 @@
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
-	//include the dompdf class
-	require_once("dompdf/dompdf_config.inc.php");
 
-	//include the Pear Mail classes for sending out emails
-	@require_once('Mail.php');
-	@require_once('Mail/mime.php');
-	
-	$base = dirname(__FILE__);
-	
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationUserDetailsInvoice.php') !== false) {
+        header("Location: ../index.php");
+        exit;
+    }
+    
+    // common notification functions
+    include("common.php");
+    
+    $base = dirname(__FILE__);
+	$base_path = "$base/templates/";
+    
 	/**
 	 * createUserDetailsInvoiceNotification()
 	 * wrapper-function to create the notification to the customer
@@ -35,16 +40,10 @@
 	 * @param		array			customer information array
 	 */
 	function createUserDetailsInvoiceNotification($customerInfo) {
-
-		global $base;
-		
-		$html = prepareNotificationTemplate($customerInfo);
-		$pdfDocument = createPDF($html);
-
-		file_put_contents("$base/out4.pdf", $pdfDocument);
-		
-		return $pdfDocument;
-		
+		global $base_path;
+        
+        $html = prepareNotificationTemplate($customerInfo);
+		return createPDF($html, $base_path);
 	}
 
 	
@@ -58,25 +57,12 @@
 	 * @param		string			from email address of the sender identity
 	 */
 	function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
-
-		global $base;
-		
-		if (empty($customerInfo['business_email']))
-			return;
-		
-		$headers = array(	"From"	=>	$from, 
-							"Subject"	=>	"User Invoice Notification",
-							"Reply-To"=> $from
-					);
-				
-		$mime = new Mail_mime();
-		$mime->setTXTBody("Notification letter of service"); 
-		$mime->addAttachment($pdfDocument, "application/pdf", "invoice.pdf", false, 'base64');
-		$body = $mime->get();
-		$headers = $mime->headers($headers);
-		$mail =& Mail::factory("smtp", $smtpInfo);
-		$mail->send($customerInfo['business_email'], $headers, $body);
-	
+        $sendTo = $customerInfo['business_email'];
+        $subject = "User Invoice Notification";
+        $body = "Notification letter of service";
+        $attachmentName = "invoice.pdf";
+        
+        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
 	}
 	
 	
@@ -88,11 +74,10 @@
 	 * @return		string			HTML notification
 	 */
 	function prepareNotificationTemplate($customerInfo) {
-	
-		global $base;
+		global $base_path;
 		
 		// the HTML template
-		$notification_template = "$base/templates/user_invoice_details.html";
+		$notification_template = $base_path . "/user_invoice_details.html";
 		$notification_html_template = file_get_contents($notification_template);
 	
 		$date = date("Y-m-d");
@@ -111,31 +96,7 @@
 		$notification_html_template = str_replace("####__BUSINESS_EMAIL__####", $business_email, $notification_html_template);
 		$notification_html_template = str_replace("####__SERVICE_PLAN_INFO__####", $service_plan_info, $notification_html_template);
 		
-
 		return $notification_html_template;
 	}
-	
-	/**
-	 * createPDF()
-	 * creates a PDF document for a given html file
-	 * 
-	 * @param 		string			the html file to convert to pdf			
-	 * @return 		string			returns the pdf in binary/string stream
-	 */
-	function createPDF($html) {
-	
-		global $base;
-		
-		// instansiate the pdf document
-		$dompdf = new DOMPDF();
-		$dompdf->set_base_path("$base/templates/");
-		$dompdf->load_html($html);
-		$dompdf->render();
-	
-		$notification_pdf = $dompdf->output();
-		
-		return $notification_pdf;
-
-	}
 
 ?>

programming_language: PHP
filename: processNotificationUserInvoice.php
change_type: ModificationType.MODIFY
diff: @@ -15,209 +15,171 @@
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
- *          Miguel García <miguelvisgarcia@gmail.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Miguel García <miguelvisgarcia@gmail.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
-	//include the dompdf class
-	require_once("dompdf/dompdf_config.inc.php");
 
-	//include the Pear Mail classes for sending out emails
-	@require_once('Mail.php');
-	@require_once('Mail/mime.php');
-	
-	$base = dirname(__FILE__);
-	
-	/**
-	 * createNotification()
-	 * wrapper-function to create the notification to the customer
-	 * 
-	 * @param		array			customer information array
-	 * @param		boolean			true for HTML output, false for PDF output
-	 * @return 		string			returns the HTML or PDF output
-	 */
-	function createNotification($customerInfo, $asHTML = false) {
+    // common notification functions
+    include("common.php");
+    
+    $base = dirname(__FILE__);
+    $base_path = "$base/templates/";
+    
+    /**
+     * createNotification()
+     * wrapper-function to create the notification to the customer
+     * 
+     * @param         array          customer information array
+     * @param         boolean        true for HTML output, false for PDF output
+     * 
+     * @return        string         returns the HTML or PDF output
+     */
+    function createNotification($customerInfo, $asHTML = false) {
+        global $base_path;
+        
+        $html = prepareNotificationTemplate($customerInfo);
+        return (($asHTML) ? $html : createPDF($html, $base_path));
+    }
 
-		global $base;
-		
-		$html = prepareNotificationTemplate($customerInfo);
-		
-		if($asHTML) {
-			$document = $html;
-		}
-		else {
-			$document = createPDF($html);
-		}
+    
+    /**
+     * emailNotification()
+     * creates an email message with the pdf and sends it
+     * 
+     * @param        string        pdf binary/string stream
+     * @param        array         customer information array
+     * @param        array         smtp server information
+     * @param        string        from email address of the sender identity
+     */
+    function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
+        $sendTo = $customerInfo['business_email'];
+        $subject = "Invoice Information";
+        $body = "Notification letter of service";
+        $attachmentName = "notification.pdf";
+        
+        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
+    }
+    
+    
+    /**
+     * prepareNotificationTemplate()
+     * reads the notification (html) template and returns the formatted (html) notification
+     * 
+     * @param         array         Array of notification information
+     * 
+     * @return        string        HTML notification
+     */
+    function prepareNotificationTemplate($customerInfo) {
+        global $base_path, $configValues;
+        
+        $templates_base = $base_path;
+        
+        // the default HTML template - OLD STYLE
+        $notification_template = $templates_base . "user_invoice.html";
+        $notification_item_template = null;
+        
+        // the default HTML template - NEW STYLE
+        if(isset($configValues['CONFIG_INVOICE_TEMPLATE'])) {
+            $notification_template = $templates_base . $configValues['CONFIG_INVOICE_TEMPLATE'];
+            
+            if(isset($configValues['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
+                $notification_item_template = $templates_base . $configValues['CONFIG_INVOICE_ITEM_TEMPLATE'];
+            }
+        }
+        
+        // the default HTML template for current location - NEW STYLE
+        if ((isset($_SESSION['location_name'])) && ($_SESSION['location_name'] != "default")) {
+            $location_name = $_SESSION['location_name'];
+            $location = $configValues['CONFIG_LOCATIONS'][$location_name];
+        
+            if (isset($location['CONFIG_INVOICE_TEMPLATE'])) {
+                $notification_template = $templates_base . $location['CONFIG_INVOICE_TEMPLATE'];
+                
+                if (isset($location['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
+                    $notification_item_template = $templates_base . $location['CONFIG_INVOICE_ITEM_TEMPLATE'];
+                }
+            }
+        }
 
-		return $document;
-	}
+        // load template for invoice
+        $notification_html_template = file_get_contents($notification_template);
+        
+        // load template for each invoice item
+        if ($notification_item_template !== null) {
+            $notification_item_html_template = file_get_contents($notification_item_template);
+        }
 
-	
-	/**
-	 * emailNotification()
-	 * creates an email message with the pdf and sends it
-	 * 
-	 * @param		string			pdf binary/string stream
-	 * @param		array			customer information array
-	 * @param		array			smtp server information
-	 * @param		string			from email address of the sender identity
-	 */
-	function emailNotification($pdfDocument, $customerInfo, $smtpInfo, $from) {
+        $date = date("Y-m-d");
+        
+        $name = $customerInfo['customer_name'];
+        $address = $customerInfo['customer_address'];
+        $phone = $customerInfo['customer_phone'];
+        $email = $customerInfo['customer_email'];
+        
+        $invoice_details = $customerInfo['invoice_details'];
+        $invoice_items = $customerInfo['invoice_items'];
+    
+        // notification date
+        $notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
+        
+        // user details
+        $notification_html_template = str_replace("####__CUSTOMER_NAME__####", $name, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $address, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $phone, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $email, $notification_html_template);
+        
+        // invoice information
+        $notification_html_template = str_replace("####__INVOICE_DETAILS__####", $invoice_details, $notification_html_template);
+        $notification_html_template = str_replace("####__INVOICE_ITEMS__####", $invoice_items, $notification_html_template);
+        
+        // customer details - NEW STYLE            
+        $notification_html_template = str_replace("[CustomerId]", $customerInfo['customerId'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerName]", $customerInfo['customerName'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerAddress]", $customerInfo['customerAddress'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerAddress2]", $customerInfo['customerAddress2'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerPhone]", $customerInfo['customerPhone'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerEmail]", $customerInfo['customerEmail'], $notification_html_template);
+        $notification_html_template = str_replace("[CustomerContact]", $customerInfo['customerContact'], $notification_html_template);
+        
+        // invoice details - NEW STYLE
+        $notification_html_template = str_replace("[InvoiceNumber]", $customerInfo['invoiceNumber'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceDate]", $customerInfo['invoiceDate'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceStatus]", $customerInfo['invoiceStatus'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceTotalBilled]", $customerInfo['invoiceTotalBilled'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoicePaid]", $customerInfo['invoicePaid'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceDue]", $customerInfo['invoiceDue'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceNotes]", $customerInfo['invoiceNotes'], $notification_html_template);
+        
+        // invoice items - NEW STYLE
+        if($notification_item_template !== null) {
+            $invoiceItems = '';
+            
+            foreach($customerInfo['invoiceItems'] as $invoiceItem) {
+                $invoiceItemTemplate = $notification_item_html_template;
+        
+                $invoiceItemTemplate = str_replace("[InvoiceItemNumber]", $invoiceItem['invoiceItemNumber'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemPlan]", $invoiceItem['invoiceItemPlan'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemNotes]", $invoiceItem['invoiceItemNotes'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemAmount]", $invoiceItem['invoiceItemAmount'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemTaxAmount]", $invoiceItem['invoiceItemTaxAmount'], $invoiceItemTemplate);
+                $invoiceItemTemplate = str_replace("[InvoiceItemTotalAmount]", $invoiceItem['invoiceItemTotalAmount'], $invoiceItemTemplate);
+                
+                $invoiceItems .= $invoiceItemTemplate;
+            }
 
-		global $base;
-		
-		if (empty($customerInfo['customer_email']))
-			return;
-		
-		$headers = array (
-			"From" => $from, 
-			"Subject" => "Invoice Information",
-			"Reply-To" => $from
-		);
-				
-		$mime = new Mail_mime();
-		$mime->setTXTBody("Notification letter of service"); 
-		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
-		$body = $mime->get();
-		$headers = $mime->headers($headers);
-		$mail =& Mail::factory("smtp", $smtpInfo);
-		$mail->send($customerInfo['customer_email'], $headers, $body);
-	}
-	
-	
-	/**
-	 * prepareNotificationTemplate()
-	 * reads the notification (html) template and returns the formatted (html) notification
-	 * 
-	 * @param		array			Array of notification information
-	 * @return		string			HTML notification
-	 */
-	function prepareNotificationTemplate($customerInfo) {
-	
-		global $base;
-		global $configValues;
-		
-		// the default HTML template - OLD STYLE
-		$notification_template = "$base/templates/user_invoice.html";
-		$notification_item_template = null;
-		
-		// the default HTML template - NEW STYLE
-		if(isset($configValues['CONFIG_INVOICE_TEMPLATE'])) {
-			$notification_template = "$base/templates/" . $configValues['CONFIG_INVOICE_TEMPLATE'];
-			
-			if(isset($configValues['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
-				$notification_item_template = "$base/templates/" . $configValues['CONFIG_INVOICE_ITEM_TEMPLATE'];
-			}
-		}
-		
-		// the default HTML template for current location - NEW STYLE
-		if((isset($_SESSION['location_name'])) && ($_SESSION['location_name'] != "default")) {
-			if(isset($configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_TEMPLATE'])) {
-				$notification_template = "$base/templates/" . $configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_TEMPLATE'];
-				
-				if(isset($configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_ITEM_TEMPLATE'])) {
-					$notification_item_template = "$base/templates/" . $configValues['CONFIG_LOCATIONS'][$_SESSION['location_name']]['CONFIG_INVOICE_ITEM_TEMPLATE'];
-				}
-			}
-		}
-
-		// load template for invoice
-		$notification_html_template = file_get_contents($notification_template);
-		
-		// load template for each invoice item
-		if($notification_item_template !== null) {
-			$notification_item_html_template = file_get_contents($notification_item_template);
-		}
-
-		$date = date("Y-m-d");
-		
-		$name = $customerInfo['customer_name'];
-		$address = $customerInfo['customer_address'];
-		$phone = $customerInfo['customer_phone'];
-		$email = $customerInfo['customer_email'];
-		
-		$invoice_details = $customerInfo['invoice_details'];
-		$invoice_items = $customerInfo['invoice_items'];
-	
-		// notification date
-		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
-		
-		// user details
-		$notification_html_template = str_replace("####__CUSTOMER_NAME__####", $name, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $address, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $phone, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $email, $notification_html_template);
-		
-		// invoice information
-		$notification_html_template = str_replace("####__INVOICE_DETAILS__####", $invoice_details, $notification_html_template);
-		$notification_html_template = str_replace("####__INVOICE_ITEMS__####", $invoice_items, $notification_html_template);
-		
-		// customer details - NEW STYLE			
-		$notification_html_template = str_replace("[CustomerId]", $customerInfo['customerId'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerName]", $customerInfo['customerName'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerAddress]", $customerInfo['customerAddress'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerAddress2]", $customerInfo['customerAddress2'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerPhone]", $customerInfo['customerPhone'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerEmail]", $customerInfo['customerEmail'], $notification_html_template);
-		$notification_html_template = str_replace("[CustomerContact]", $customerInfo['customerContact'], $notification_html_template);
-		
-		// invoice details - NEW STYLE
-		$notification_html_template = str_replace("[InvoiceNumber]", $customerInfo['invoiceNumber'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceDate]", $customerInfo['invoiceDate'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceStatus]", $customerInfo['invoiceStatus'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceTotalBilled]", $customerInfo['invoiceTotalBilled'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoicePaid]", $customerInfo['invoicePaid'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceDue]", $customerInfo['invoiceDue'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceNotes]", $customerInfo['invoiceNotes'], $notification_html_template);
-		
-		// invoice items - NEW STYLE
-		if($notification_item_template !== null) {
-			$invoiceItems = '';
-			
-			foreach($customerInfo['invoiceItems'] as $invoiceItem) {
-				$invoiceItemTemplate = $notification_item_html_template;
-		
-				$invoiceItemTemplate = str_replace("[InvoiceItemNumber]", $invoiceItem['invoiceItemNumber'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemPlan]", $invoiceItem['invoiceItemPlan'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemNotes]", $invoiceItem['invoiceItemNotes'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemAmount]", $invoiceItem['invoiceItemAmount'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemTaxAmount]", $invoiceItem['invoiceItemTaxAmount'], $invoiceItemTemplate);
-				$invoiceItemTemplate = str_replace("[InvoiceItemTotalAmount]", $invoiceItem['invoiceItemTotalAmount'], $invoiceItemTemplate);
-				
-				$invoiceItems .= $invoiceItemTemplate;
-			}
-
-			$notification_html_template = str_replace("[InvoiceItems]", $invoiceItems, $notification_html_template);
-		}
-		
-		// more invoice details - NEW STYLE
-		$notification_html_template = str_replace("[InvoiceTotalAmount]", $customerInfo['invoiceTotalAmount'], $notification_html_template);
-		$notification_html_template = str_replace("[InvoiceTotalTax]", $customerInfo['invoiceTotalTax'], $notification_html_template);
-		
-		return $notification_html_template;
-	}
-	
-	/**
-	 * createPDF()
-	 * creates a PDF document for a given html file
-	 * 
-	 * @param 		string			the html file to convert to pdf			
-	 * @return 		string			returns the pdf in binary/string stream
-	 */
-	function createPDF($html) {
-	
-		global $base;
-		
-		// instansiate the pdf document
-		$dompdf = new DOMPDF();
-		$dompdf->set_base_path("$base/templates/");
-		$dompdf->load_html($html);
-		$dompdf->render();
-	
-		$notification_pdf = $dompdf->output();
-		
-		return $notification_pdf;
-	}
+            $notification_html_template = str_replace("[InvoiceItems]", $invoiceItems, $notification_html_template);
+        }
+        
+        // more invoice details - NEW STYLE
+        $notification_html_template = str_replace("[InvoiceTotalAmount]", $customerInfo['invoiceTotalAmount'], $notification_html_template);
+        $notification_html_template = str_replace("[InvoiceTotalTax]", $customerInfo['invoiceTotalTax'], $notification_html_template);
+        
+        // this fix has been set in place according to this thread:
+        // https://stackoverflow.com/questions/37521775/dompdf-error-no-block-level-parent-found-not-good
+        return str_replace("\n", "", $notification_html_template);
+    }
 
 ?>

programming_language: PHP
filename: processNotificationWelcome.php
change_type: ModificationType.MODIFY
diff: @@ -1,4 +1,4 @@
-<?php
+<?php 
 /*
  *********************************************************************************************************
  * daloRADIUS - RADIUS Web Platform
@@ -15,105 +15,70 @@
  *
  *********************************************************************************************************
  *
- * Authors:	Liran Tal <liran@enginx.com>
+ * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
-	//include the dompdf class
-	require_once("dompdf/dompdf_config.inc.php");
 
-	//include the Pear Mail classes for sending out emails
-	@require_once('Mail.php');
-	@require_once('Mail/mime.php');
-	
-	$base = dirname(__FILE__);
-	
-	/**
-	 * sendWelcomeNotification()
-	 * wrapper-function to send notification to the customer
-	 * 
-	 * @param		array			customer information array
-	 * @param		array			smtp server information
-	 * @param		string			from email address of the sender identity
-	 */
-	function sendWelcomeNotification($customerInfo, $smtpInfo, $from) {
+    // prevent this file to be directly accessed
+    if (strpos($_SERVER['PHP_SELF'], '/notifications/processNotificationWelcome.php') !== false) {
+        header("Location: ../index.php");
+        exit;
+    }
+    
+    // common notification functions
+    include("common.php");
+    
+    $base = dirname(__FILE__);
+    $base_path = "$base/templates/";
+    
+    /**
+     * sendWelcomeNotification()
+     * wrapper-function to send notification to the customer
+     * 
+     * @param        array         customer information array
+     * @param        array         smtp server information
+     * @param        string        from email address of the sender identity
+     */
+    function sendWelcomeNotification($customerInfo, $smtpInfo, $from) {
+        $sendTo = $customerInfo['business_email'];
+        $subject = "Welcome new customer!";
+        $body = "Notification letter of service";
+        $attachmentName = "notification.pdf";
+        
+        return send_notification_via_email($pdfDocument, $smtpInfo, $sendTo, $from, $subject, $body, $attachmentName);
+    }
 
-		global $base;
-		
-		if (empty($customerInfo['customer_email']))
-			return;
-		
-		$headers = array(	"From"	=>	$from, 
-							"Subject"	=>	"Welcome new customer!",
-							"Reply-To"=> $from
-					);
-		
-		$html = prepareNotificationTemplate($customerInfo);
-		$pdfDocument = createPDF($html);
-		
-		$mime = new Mail_mime(); 
-		$mime->setTXTBody("Notification letter of service"); 
-		$mime->addAttachment($pdfDocument, "application/pdf", "notification.pdf", false, 'base64');
-		$body = $mime->get();
-		$headers = $mime->headers($headers);
-		$mail =& Mail::factory("smtp", $smtpInfo);
-		$mail->send($customerInfo['customer_email'], $headers, $body);		
-		
-	}
+    
+    /**
+     * prepareNotificationTemplate()
+     * reads the notification (html) template and returns the formatted (html) notification
+     * 
+     * @param         array         Array of notification information
+     * @return        string        HTML notification
+     */
+    function prepareNotificationTemplate($customerInfo) {
+        global $base_path;
+        
+        // the HTML template
+        $notification_template = $base_path . "welcome.html";
+        $notification_html_template = file_get_contents($notification_template);
+    
+        $date = date("Y-m-d");
+    
+        $customer_name = $customerInfo['customer_name'];
+        $customer_address = $customerInfo['customer_address']; 
+        $customer_phone = $customerInfo['customer_phone'];
+        $customer_email = $customerInfo['customer_email'];
+    
+        $notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_NAME__####", $customer_name, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $customer_address, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $customer_phone, $notification_html_template);
+        $notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $customer_email, $notification_html_template);
 
-	
-	/**
-	 * prepareNotificationTemplate()
-	 * reads the notification (html) template and returns the formatted (html) notification
-	 * 
-	 * @param		array			Array of notification information
-	 * @return		string			HTML notification
-	 */
-	function prepareNotificationTemplate($customerInfo) {
-	
-		global $base;
-		
-		// the HTML template
-		$notification_template = "$base/templates/welcome.html";
-		$notification_html_template = file_get_contents($notification_template);
-	
-		$date = date("Y-m-d");
-	
-		$customer_name = $customerInfo['customer_name'];
-		$customer_address = $customerInfo['customer_address']; 
-		$customer_phone = $customerInfo['customer_phone'];
-		$customer_email = $customerInfo['customer_email'];
-	
-		$notification_html_template = str_replace("####__INVOICE_CREATION_DATE__####", $date, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_NAME__####", $customer_name, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_ADDRESS__####", $customer_address, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_PHONE__####", $customer_phone, $notification_html_template);
-		$notification_html_template = str_replace("####__CUSTOMER_EMAIL__####", $customer_email, $notification_html_template);
-
-		return $notification_html_template;
-	}
-	
-	/**
-	 * createPDF()
-	 * creates a PDF document for a given html file
-	 * 
-	 * @param 		string			the html file to convert to pdf			
-	 * @return 		string			returns the pdf in binary/string stream
-	 */
-	function createPDF($html) {
-	
-		global $base;
-		
-		// instansiate the pdf document
-		$dompdf = new DOMPDF();
-		$dompdf->set_base_path("$base/templates/");
-		$dompdf->load_html($html);
-		$dompdf->render();
-	
-		$notification_pdf = $dompdf->output();
-		
-		return $notification_pdf;
-
-	}
+        return $notification_html_template;
+    }
 
 ?>

programming_language: PHP
filename: rep-batch-details.php
change_type: ModificationType.MODIFY
diff: @@ -16,6 +16,7 @@
  *********************************************************************************************************
  *
  * Authors:    Liran Tal <liran@enginx.com>
+ *             Filippo Lauria <filippo.lauria@iit.cnr.it>
  *
  *********************************************************************************************************
  */
@@ -39,6 +40,9 @@
     $logQuery = "performed query for batch [$batch_name] on page: ";
     $logDebugSQL = "";
 
+    // set session's page variable
+    $_SESSION['PREV_LIST_PAGE'] = $_SERVER['REQUEST_URI'];
+
     include_once("lang/main.php");
     
     // first table
@@ -82,23 +86,14 @@
 
     // print HTML prologue   
     $title = t('Intro','repbatchdetails.php');
+    $help = t('helpPage','repbatchdetails');
     
     print_html_prologue($title, $langCode);
 
     include ("menu-reports-batch.php");
-      
-?>    
-
-        <div id="contentnorightbar">
-            <h2 id="Intro">
-                <a href="#"  onclick="javascript:toggleShowDiv('helpPage')">
-                    <?= t('Intro','repbatchdetails.php'); ?><h144>&#x2754;</h144>
-                </a>
-            </h2>
-
-            <div id="helpPage" style="display:none;visibility:visible"><?= t('helpPage','repbatchdetails') ?><br></div>
-
-<?php
+    
+    echo '<div id="contentnorightbar">';
+    print_title_and_help($title, $help);
 
     include('library/opendb.php');
     include('include/management/pages_common.php');
@@ -148,7 +143,7 @@
             <tr style="background-color: white">
                 <td style="text-align: right" colspan="<?= $colspan1 ?>">
                     <input class="button" type="button" value="Download Invoice"
-                        onclick="location.href='include/common/notificationsBatchDetails.php?batch_name=<?= urlencode($batch_name_enc) ?>&destination=download'">
+                        onclick="window.open('include/common/notificationsBatchDetails.php?batch_name=<?= urlencode($batch_name_enc) ?>&destination=download')">
                     <input class="button" type="button" value="Email Invoice to Business/Hotspot"
                         onclick="location.href='include/common/notificationsBatchDetails.php?batch_name=<?= urlencode($batch_name_enc) ?>&destination=email'">
                     <input class="button" type="button" value="CSV Export"
@@ -318,26 +313,14 @@
     
     include('library/closedb.php');
 
-?>
-
-</div><!-- #contentnorightbar -->
-        
-        <div id="footer">
-<?php
     include('include/config/logging.php');
-    include('page-footer.php');
+    
+    $inline_extra_js = "
+var tooltipObj = new DHTMLgoodies_formTooltip();
+tooltipObj.setTooltipPosition('right');
+tooltipObj.setPageBgColor('#EEEEEE');
+tooltipObj.setTooltipCornerSize(15);
+tooltipObj.initFormFieldTooltip()";
+    
+    print_footer_and_html_epilogue($inline_extra_js);
 ?>
-        </div><!-- #footer -->
-    </div>
-</div>
-
-<script>
-    var tooltipObj = new DHTMLgoodies_formTooltip();
-    tooltipObj.setTooltipPosition('right');
-    tooltipObj.setPageBgColor('#EEEEEE');
-    tooltipObj.setTooltipCornerSize(15);
-    tooltipObj.initFormFieldTooltip();
-</script>
-
-</body>
-</html>

programming_language: PHP
filename: rep-online.php
change_type: ModificationType.MODIFY
diff: @@ -189,7 +189,7 @@
         // printTableFormControls function parameter
         $action = "mng-del.php";
 ?>
-<form name="listall" method="GET" action="<?= $action ?>">
+<form name="listall" method="POST" action="<?= $action ?>">
     <table border="0" class="table1">
         <thead>
             <tr style="background-color: white">
@@ -301,6 +301,9 @@
 ?>
 
     </table>
+    
+    <input name="csrf_token" type="hidden" value="<?= dalo_csrf_token() ?>">
+    
 </form>    
 
 <?php

programming_language: PHP
filename: rep-username.php
change_type: ModificationType.MODIFY
diff: @@ -120,6 +120,8 @@
 
     <tbody>
 <?php
+            $csrf_token = dalo_csrf_token();
+
             while ($row = $res->fetchRow()) {
                 $rowlen = count($row);
                 
@@ -129,8 +131,17 @@
                 }
                 
                 $this_username = htmlspecialchars($row[1], ENT_QUOTES, 'UTF-8');
-                printf('<td><a href="mng-edit.php?username=%s">%s</a>&nbsp;<a href="mng-del.php?username=%s">%s</a></td>',
-                       urlencode($this_username), t('all','edit'), urlencode($this_username), t('all','del'));
+                echo '<td>';
+                $formId = $this_username . "-form-del";
+                printf('<form id="%s" style="display: none" method="POST" action="mng-del.php">', $formId);
+                printf('<input type="hidden" name="username[]" value="%s">', $this_username);
+                printf('<input type="hidden" name="csrf_token" value="%s">', $csrf_token);
+                echo '</form>';
+                
+                $onclick = sprintf("document.getElementById('%s').submit()", $formId);
+                printf('<a href="mng-edit.php?username=%s">%s</a>&nbsp;<a href="#" onclick="%s">%s</a>',
+                       urlencode($this_username), t('all','edit'), $onclick, t('all','del'));
+                echo '</td>';
                 echo "</tr>";
             }
 ?>

programming_language: PHP