Explorar el Código

Add MMS to email. Change default settings to library functions. (#130)

* Add MMS to email.  Change default settings to library functions.

- Add limited support for MMS-to-email.
- Move email address location Contacts to v_sms_destinations table.
- Move email handling into its own function.
- Change loading of default/domain settings to use library functions.
- Change from dynamic to parameterized queries.
- Add check for target extension's registration status for inbound SMS.
- Add check for target extension's registration status for local messages (chatplan/default.xml)
- Add filter for message type to catch delivery status notifications sent by some softphone clients.
- Fix bug for multiple-line messages.
- Fix some message encoding/decoding problems.

* Update README.md

Email/MMS setup info.

* Update index.lua

Additional fix to multi-line and body encoding.
jonathanblack1000 hace 5 años
padre
commit
e886bc51d5

+ 6 - 0
sms/README.md

@@ -18,5 +18,11 @@
 15. Add your carrier's IPs in an ACL
 16. Add your callback URL on your carrier to IE for twillio it would be: https://YOURDOMAIN/app/sms/hook/sms_hook_twilio.php
     - Note: You will need to have a valid certificate to use Twilio. If you need a certificate, consider using Let's Encrypt and certbot. It’s fast and free. 
+17. For email delivery support, it uses the default setting email->smtp_from, so make sure that this is set appropriately.
+18. For MMS email delivery, it will use the default setting sms->mms_attatement_temp_path, if this is set.  If not, it will try to use '/var/www/fusionpbx/app/sms/tmp/'
+    as the temporary storage for the attachments.  Please make sure that you create the appropriate temp folder and change ownership to www-data/www-data.
 
 Send and receive!
+
+NOTE: It is not recommended to use this app with versions of Freeswitch prior to 1.8 if you are installing in a clustered environment.  
+There is a bug in earlier versions of Freeswitch that can cause it to crash in certain situation when using SMS.

+ 37 - 0
sms/app_config.php

@@ -98,6 +98,14 @@
         $apps[$x]['default_settings'][$y]['default_setting_enabled'] = 'true';
         $apps[$x]['default_settings'][$y]['default_setting_description'] = '';
         $y++;
+		$apps[$x]['default_settings'][$y]['default_setting_uuid'] = 'd1e19c12-fdb8-4dfa-b21b-213250bf4b7b';
+		$apps[$x]['default_settings'][$y]['default_setting_category'] = 'sms';
+		$apps[$x]['default_settings'][$y]['default_setting_subcategory'] = 'carriers';
+		$apps[$x]['default_settings'][$y]['default_setting_name'] = 'array';
+		$apps[$x]['default_settings'][$y]['default_setting_value'] = 'peerlees';
+		$apps[$x]['default_settings'][$y]['default_setting_enabled'] = 'true';
+		$apps[$x]['default_settings'][$y]['default_setting_description'] = '';
+		$y++;
 
 		$apps[$x]['default_settings'][$y]['default_setting_uuid'] = 'e997203c-ca48-45b4-828d-e347ff66fa7c';
 		$apps[$x]['default_settings'][$y]['default_setting_category'] = 'sms';
@@ -263,6 +271,31 @@
 		$apps[$x]['default_settings'][$y]['default_setting_enabled'] = 'false';
 		$apps[$x]['default_settings'][$y]['default_setting_description'] = '';
 		$y++;
+		$apps[$x]['default_settings'][$y]['default_setting_uuid'] = '2c2a1671-9213-4f2e-8254-a47cda4e7b05';
+		$apps[$x]['default_settings'][$y]['default_setting_category'] = 'sms';
+		$apps[$x]['default_settings'][$y]['default_setting_subcategory'] = 'peerless_access_key';
+		$apps[$x]['default_settings'][$y]['default_setting_name'] = 'text';
+		$apps[$x]['default_settings'][$y]['default_setting_value'] = '';
+		$apps[$x]['default_settings'][$y]['default_setting_enabled'] = 'false';
+		$apps[$x]['default_settings'][$y]['default_setting_description'] = '';
+		$y++;
+		$apps[$x]['default_settings'][$y]['default_setting_uuid'] = '24bf5a19-947a-4b82-8888-1657773aca12';
+		$apps[$x]['default_settings'][$y]['default_setting_category'] = 'sms';
+		$apps[$x]['default_settings'][$y]['default_setting_subcategory'] = 'peerless_secret_key';
+		$apps[$x]['default_settings'][$y]['default_setting_name'] = 'text';
+		$apps[$x]['default_settings'][$y]['default_setting_value'] = '';
+		$apps[$x]['default_settings'][$y]['default_setting_enabled'] = 'false';
+		$apps[$x]['default_settings'][$y]['default_setting_description'] = '';
+		$y++;
+
+		$apps[$x]['default_settings'][$y]['default_setting_uuid'] = 'c6358f2b-96f9-45df-bd0f-2e9464474a8e';
+		$apps[$x]['default_settings'][$y]['default_setting_category'] = 'sms';
+		$apps[$x]['default_settings'][$y]['default_setting_subcategory'] = 'mms_attachment_temp_path';
+		$apps[$x]['default_settings'][$y]['default_setting_name'] = 'text';
+		$apps[$x]['default_settings'][$y]['default_setting_value'] = '';
+		$apps[$x]['default_settings'][$y]['default_setting_enabled'] = 'true';
+		$apps[$x]['default_settings'][$y]['default_setting_description'] = '';
+		$y++;
 
 	//schema details
 		$y=0;
@@ -360,5 +393,9 @@
 		$apps[$x]['db'][$y]['fields'][$z]['type'] = "text";
 		$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "";
 		$z++;
+		$apps[$x]['db'][$y]['fields'][$z]['name'] = "email";
+		$apps[$x]['db'][$y]['fields'][$z]['type'] = "text";
+		$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "";
+		$z++;
 
 ?>

+ 26 - 0
sms/app_languages.php

@@ -156,6 +156,32 @@ $text['description-chatplan_detail_data']['de-at'] = "";
 $text['description-chatplan_detail_data']['ar-eg'] = "";
 $text['description-chatplan_detail_data']['he'] = "";
 
+$text['label-sms_email']['en-us'] = "Email Address";
+$text['label-sms_email']['es-cl'] = "";
+$text['label-sms_email']['pt-pt'] = "";
+$text['label-sms_email']['fr-fr'] = "";
+$text['label-sms_email']['pt-br'] = "";
+$text['label-sms_email']['pl'] = " ";
+$text['label-sms_email']['uk'] = "";
+$text['label-sms_email']['sv-se'] = "";
+$text['label-sms_email']['ro'] = "";
+$text['label-sms_email']['de-at'] = "";
+$text['label-sms_email']['ar-eg'] = "";
+$text['label-sms_email']['he'] = "";
+
+$text['description-sms_email']['en-us'] = "For email delivery of inbound SMS messages";
+$text['description-sms_email']['es-cl'] = "";
+$text['description-sms_email']['pt-pt'] = "";
+$text['description-sms_email']['fr-fr'] = "";
+$text['description-sms_email']['pt-br'] = "";
+$text['description-sms_email']['pl'] = " ";
+$text['description-sms_email']['uk'] = "";
+$text['description-sms_email']['sv-se'] = "";
+$text['description-sms_email']['ro'] = "";
+$text['description-sms_email']['de-at'] = "";
+$text['description-sms_email']['ar-eg'] = "";
+$text['description-sms_email']['he'] = "";
+
 $text['button-mdr']['en-us'] = "MDRs";
 $text['button-mdr']['es-cl'] = "";
 $text['button-mdr']['pt-pt'] = "";

+ 1 - 1
sms/hook/sms_hook_telnyx.php

@@ -12,7 +12,7 @@ if (check_acl()) {
 			error_log('[SMS] REQUEST: ' .  print_r($data, true));
 		}
 		$to = intval(preg_replace('/(^[\+][1])/','', $data->to));
-		route_and_send_sms($data->from, $to, $data->body);
+		route_and_send_sms($data->from, $to, $data->body, $data->media);
 	} else {
 	  die("no");
 	}

+ 173 - 141
sms/resources/install/scripts/app/sms/index.lua

@@ -49,6 +49,19 @@
 		return s
 	end
 
+	local hex_to_char = function(x)
+	  return string.char(tonumber(x, 16))
+	end
+
+	local urldecode = function(url)
+	  if url == nil then
+	    return
+	  end
+	  url = url:gsub("+", " ")
+	  url = url:gsub("%%(%x%x)", hex_to_char)
+	  return url
+	end
+
 --define uuid function
 	local random = math.random;
 	local function uuid()
@@ -59,6 +72,16 @@
 		end)
 	end
 
+--define encoding function
+	function encodeChar(chr)
+		return string.format("%%%X",string.byte(chr))
+	end
+
+	function encodeString(str)
+		local output, t = string.gsub(str,"[^%w]",encodeChar)
+		return output
+	end
+
 --get the argv values
 	script_name = argv[0];
 	direction = argv[2];
@@ -72,111 +95,122 @@
 		to = argv[3];
 		from = argv[4];
 		body = argv[5];
+		mailsent = argv[6];
 		domain_name = string.match(to,'%@+(.+)');
 		extension = string.match(to,'%d+');
-
+		if (body ~= nil) then
+			body = urldecode(body);
+		end
+		savebody = body;
+		body = body:gsub('<br>','\n');
+		
 		if (debug["info"]) then
-			freeswitch.consoleLog("notice", "[sms] DIRECTION: " .. direction .. "\n");
 			freeswitch.consoleLog("notice", "[sms] TO: " .. to .. "\n");
 			freeswitch.consoleLog("notice", "[sms] Extension: " .. extension .. "\n");
 			freeswitch.consoleLog("notice", "[sms] FROM: " .. from .. "\n");
 			freeswitch.consoleLog("notice", "[sms] BODY: " .. body .. "\n");
 			freeswitch.consoleLog("notice", "[sms] DOMAIN_NAME: " .. domain_name .. "\n");
+			if (mailsent == nil) then
+				freeswitch.consoleLog("notice", "[sms] MAILSENT (already): nil\n");
+			else
+				freeswitch.consoleLog("notice", "[sms] MAILSENT (already): " .. mailsent .. "\n");
+			end
+				
 		end
 
-		local event = freeswitch.Event("CUSTOM", "SMS::SEND_MESSAGE");
-		event:addHeader("proto", "sip");
-		event:addHeader("dest_proto", "sip");
-		event:addHeader("from", "sip:" .. from);
-		event:addHeader("from_user", from);
-		event:addHeader("from_host", domain_name);
-		event:addHeader("from_full", "sip:" .. from .."@".. domain_name);
-		event:addHeader("sip_profile","internal");
-		event:addHeader("to", to);
-		event:addHeader("to_user", extension);
-		event:addHeader("to_host", domain_name);
-		event:addHeader("subject", "SIMPLE MESSAGE");
-		event:addHeader("type", "text/plain");
-		event:addHeader("hint", "the hint");
-		event:addHeader("replying", "true");
-		event:addHeader("DP_MATCH", to);
-		event:addBody(body);
+		--See if target ext is registered.
+		extension_status = "sofia_contact " .. to;
+		reply = api:executeString(extension_status);
+		--freeswitch.consoleLog("NOTICE", "[sms] Ext status: "..reply .. "\n");
+		if (reply == "error/user_not_registered") then
+			freeswitch.consoleLog("NOTICE", "[sms] Target extension "..to.." is not registered, not sending via SIMPLE.\n");
+		else
+			local event = freeswitch.Event("CUSTOM", "SMS::SEND_MESSAGE");
+			event:addHeader("proto", "sip");
+			event:addHeader("dest_proto", "sip");
+			event:addHeader("from", "sip:" .. from);
+			event:addHeader("from_user", from);
+			event:addHeader("from_host", domain_name);
+			event:addHeader("from_full", "sip:" .. from .."@".. domain_name);
+			event:addHeader("sip_profile","internal");
+			event:addHeader("to", to);
+			event:addHeader("to_user", extension);
+			event:addHeader("to_host", domain_name);
+			event:addHeader("subject", "SIMPLE MESSAGE");
+			event:addHeader("type", "text/plain");
+			event:addHeader("hint", "the hint");
+			event:addHeader("replying", "true");
+			event:addHeader("DP_MATCH", to);
+			event:addBody(body);
 
-		if (debug["info"]) then
-			freeswitch.consoleLog("info", event:serialize());
+			if (debug["info"]) then
+				freeswitch.consoleLog("info", event:serialize() .. "\n");
+			end
+			event:fire();
 		end
-		event:fire();
 		to = extension;
-		
-		--Send inbound SMS via email delivery
-		if (domain_uuid == nil) then
-			--get the domain_uuid using the domain name required for multi-tenant
-				if (domain_name ~= nil) then
-					sql = "SELECT domain_uuid FROM v_domains ";
-					sql = sql .. "WHERE domain_name = :domain_name and domain_enabled = 'true' ";
-					local params = {domain_name = domain_name}
 
-					if (debug["sql"]) then
-						freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
-					end
-					status = dbh:query(sql, params, function(rows)
-						domain_uuid = rows["domain_uuid"];
-					end);
-				end
-		end
-		if (domain_uuid == nil) then
-			freeswitch.consoleLog("notice", "[sms] domain_uuid is nill, cannot send sms to email.");
-		else
-			sql = "SELECT v_contact_emails.email_address ";
-			sql = sql .. "from v_extensions, v_extension_users, v_users, v_contact_emails ";
-			sql = sql .. "where v_extensions.extension = :toext and v_extensions.domain_uuid = :domain_uuid and v_extensions.extension_uuid = v_extension_users.extension_uuid ";
-			sql = sql .. "and v_extension_users.user_uuid = v_users.user_uuid and v_users.contact_uuid = v_contact_emails.contact_uuid ";
-			sql = sql .. "and (v_contact_emails.email_label = 'sms' or v_contact_emails.email_label = 'SMS')";
-			local params = {toext = extension, domain_uuid = domain_uuid}
+		if (not mailsent == 1) then
+			--Send inbound SMS via email delivery 
+			-- This is legacy code retained for backwards compatibility.  See /var/www/fusionpbx/app/sms/sms_email.php for current.
+			if (domain_uuid == nil) then
+				--get the domain_uuid using the domain name required for multi-tenant
+					if (domain_name ~= nil) then
+						sql = "SELECT domain_uuid FROM v_domains ";
+						sql = sql .. "WHERE domain_name = :domain_name and domain_enabled = 'true' ";
+						local params = {domain_name = domain_name}
 
-			if (debug["sql"]) then
-				freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
+						if (debug["sql"]) then
+							freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
+						end
+						status = dbh:query(sql, params, function(rows)
+							domain_uuid = rows["domain_uuid"];
+						end);
+					end
 			end
-			status = dbh:query(sql, params, function(rows)
-				send_to_email_address = rows["email_address"];
-			end);
-
-			--sql = "SELECT domain_setting_value FROM v_domain_settings ";
-			--sql = sql .. "where domain_setting_category = 'sms' and domain_setting_subcategory = 'send_from_email_address' and domain_setting_enabled = 'true' and domain_uuid = :domain_uuid";
-			--local params = {domain_uuid = domain_uuid}
+			if (domain_uuid == nil) then
+				freeswitch.consoleLog("notice", "[sms] domain_uuid is nill, cannot send sms to email.");
+			else
+				sql = "SELECT v_contact_emails.email_address ";
+				sql = sql .. "from v_extensions, v_extension_users, v_users, v_contact_emails ";
+				sql = sql .. "where v_extensions.extension = :toext and v_extensions.domain_uuid = :domain_uuid and v_extensions.extension_uuid = v_extension_users.extension_uuid ";
+				sql = sql .. "and v_extension_users.user_uuid = v_users.user_uuid and v_users.contact_uuid = v_contact_emails.contact_uuid ";
+				sql = sql .. "and (v_contact_emails.email_label = 'sms' or v_contact_emails.email_label = 'SMS')";
+				local params = {toext = extension, domain_uuid = domain_uuid}
 
-			--if (debug["sql"]) then
-			--	freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
-			--end
-			--status = dbh:query(sql, params, function(rows)
-			--	send_from_email_address = rows["domain_setting_value"];
-			--end);
-			-- Tried setting the "from" address, above, but default email facility is overriding with global/domain-level default settings.
-			send_from_email_address = '[email protected]'  -- this gets overridden if using v_mailto.php
-			
-			if (send_to_email_address ~= nill and send_from_email_address ~= nill) then
-				subject = 'Text Message from: ' .. from;
-				emailbody = 'To: ' .. to .. '<br>Msg:' .. body;
-				if (debug["info"]) then
-					freeswitch.consoleLog("info", emailbody);
+				if (debug["sql"]) then
+					freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
 				end
-				--luarun email.lua send_to_email_address send_from_email_address '' subject emailbody;
-				--replace the &#39 with a single quote
-					emailbody = emailbody:gsub("&#39;", "'");
+				status = dbh:query(sql, params, function(rows)
+					send_to_email_address = rows["email_address"];
+				end);
 
-				--replace the &#34 with double quote
-					emailbody = emailbody:gsub("&#34;", [["]]);
+				send_from_email_address = '[email protected]'  -- this gets overridden if using v_mailto.php
 
-				--send the email
-					freeswitch.email(send_to_email_address,
-						send_from_email_address,
-						"To: "..send_to_email_address.."\nFrom: "..send_from_email_address.."\nX-Headers: \nSubject: "..subject,
-						emailbody
-						);
-			end
-		end 
+				if (send_to_email_address ~= nill and send_from_email_address ~= nill) then
+					subject = 'Text Message from: ' .. from;
+					emailbody = 'To: ' .. to .. '<br>Msg:' .. body;
+					if (debug["info"]) then
+						freeswitch.consoleLog("info", emailbody);
+					end
+					--luarun email.lua send_to_email_address send_from_email_address '' subject emailbody;
+					--replace the &#39 with a single quote
+						emailbody = emailbody:gsub("&#39;", "'");
+
+					--replace the &#34 with double quote
+						emailbody = emailbody:gsub("&#34;", [["]]);
+
+					--send the email
+						freeswitch.email(send_to_email_address,
+							send_from_email_address,
+							"To: "..send_to_email_address.."\nFrom: "..send_from_email_address.."\nX-Headers: \nSubject: "..subject,
+							emailbody
+							);
+				end
+			end 
+		end
 
-		elseif direction == "outbound" then
+	elseif direction == "outbound" then
 		if (argv[3] ~= nil) then
 			to_user = argv[3];
 			to_user = to_user:gsub("^+?sip%%3A%%40","");
@@ -204,6 +238,9 @@
 		else
 			body = message:getBody();
 		end
+		if (debug["info"]) then
+			freeswitch.consoleLog("notice", "[sms] BODY-raw: " .. body .. "\n");
+		end
 		--Clean body up for Groundwire send
 		smsraw = body;
 		smstempst, smstempend = string.find(smsraw, 'Content%-length:');
@@ -218,7 +255,9 @@
 			end
 		end
 		body = body:gsub('%"','');
-		--body = body:gsub('\r\n',' ');
+		savebody = body;
+		--body = encodeString((body));
+		body = body:gsub('\n','\\n');
 
 		if (debug["info"]) then
 			if (message ~= nil) then
@@ -247,6 +286,7 @@
 				end
 		end
 		freeswitch.consoleLog("notice", "[sms] DOMAIN_UUID: " .. domain_uuid .. "\n");
+
 		if (outbound_caller_id_number == nil) then
 			--get the outbound_caller_id_number using the domain_uuid and the extension number
 				if (domain_uuid ~= nil) then
@@ -286,50 +326,60 @@
 				end
 		end
 		
-		sql = "SELECT default_setting_value FROM v_default_settings ";
-		sql = sql .. "where default_setting_category = 'sms' and default_setting_subcategory = '" .. carrier .. "_access_key' and default_setting_enabled = 'true'";
-		local params = {carrier = carrier}
-
-		if (debug["sql"]) then
-			freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
-		end
-		status = dbh:query(sql, function(rows)
-			access_key = rows["default_setting_value"];
-		end);
-
-		sql = "SELECT default_setting_value FROM v_default_settings ";
-		sql = sql .. "where default_setting_category = 'sms' and default_setting_subcategory = '" .. carrier .. "_secret_key' and default_setting_enabled = 'true'";
-
-		if (debug["sql"]) then
-			freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
+		--get settings 
+		require "resources.functions.settings";
+		settings = settings(domain_uuid);
+		if (settings['sms'] ~= nil) then
+			if (settings['sms'][carrier..'_access_key'] ~= nil) then
+				if (settings['sms'][carrier..'_access_key']['text'] ~= nil) then
+					access_key = settings['sms'][carrier..'_access_key']['text']
+				end
+			end
+			if (settings['sms'][carrier..'_secret_key'] ~= nil) then
+				if (settings['sms'][carrier..'_secret_key']['text'] ~= nil) then
+					secret_key = settings['sms'][carrier..'_secret_key']['text']
+				end
+			end
+			if (settings['sms'][carrier..'_api_url'] ~= nil) then
+				if (settings['sms'][carrier..'_api_url']['text'] ~= nil) then
+					api_url = settings['sms'][carrier..'_api_url']['text']
+				end
+			end
+			if (settings['sms'][carrier..'_username'] ~= nil) then
+				if (settings['sms'][carrier..'_username']['text'] ~= nil) then
+					username = settings['sms'][carrier..'_username']['text']
+				end
+			end
+			if (settings['sms'][carrier..'_delivery_status_webhook_url'] ~= nil) then
+				if (settings['sms'][carrier..'_delivery_status_webhook_url']['text'] ~= nil) then
+					delivery_status_webhook_url = settings['sms'][carrier..'_delivery_status_webhook_url']['text']
+				end
+			end
 		end
-		status = dbh:query(sql, function(rows)
-			secret_key = rows["default_setting_value"];
-		end);
-
-		sql = "SELECT default_setting_value FROM v_default_settings ";
-		sql = sql .. "where default_setting_category = 'sms' and default_setting_subcategory = '" .. carrier .. "_api_url' and default_setting_enabled = 'true'";
-		if (debug["sql"]) then
-			freeswitch.consoleLog("notice", "[sms] SQL: " .. sql .. "\n");
+		if (debug["info"]) then
+			if (access_key ~= nil) then freeswitch.consoleLog("notice", "[sms] access_key: " .. access_key .. "\n") end;
+			if (secret_key ~= nil) then freeswitch.consoleLog("notice", "[sms] secret_key: " .. secret_key .. "\n") end;
+			if (api_url ~= nil) then freeswitch.consoleLog("notice", "[sms] api_url: " .. api_url .. "\n") end;
+			if (username ~= nil) then freeswitch.consoleLog("notice", "[sms] username: " .. username .. "\n") end;
+			if (delivery_status_webhook_url ~= nil) then freeswitch.consoleLog("notice", "[sms] delivery_status_webhook_url: " .. delivery_status_webhook_url .. "\n") end;
 		end
-		status = dbh:query(sql, function(rows)
-			api_url = rows["default_setting_value"];
-		end);
+		
+			
 
-		--Check for xml content
+		--Check for xml content or delivery status notification type
 		smstempst, smstempend = string.find(body, '<%?xml');
 		if (smstempst ~= nil) then freeswitch.consoleLog("notice", "[sms] smstempst = '" .. smstempst .. "\n") end;
 		if (smstempend ~= nil) then freeswitch.consoleLog("notice", "[sms] smstempend = '" .. smstempend .. "\n") end;
-		if (smstempst == nil) then 
+		mdn = (smstempst ~= nil); --message delivery notification
+		msgtype = message:getHeader("type");
+		if (msgtype ~= nil and string.find(msgtype, "imdn") ~= nil) then mdn = true end;
+		if (not mdn) then 
 			-- No XML content, continue processing
 			if (carrier == "flowroute") then
 				cmd = "curl -u ".. access_key ..":" .. secret_key .. " -H \"Content-Type: application/json\" -X POST -d '{\"to\":\"" .. to .. "\",\"from\":\"" .. outbound_caller_id_number .."\",\"body\":\"" .. body .. "\"}' " .. api_url;
-			
-		elseif (carrier == "peerless") then	
-   	        cmd = "curl -u" .. access_key .. ":" .. secret_key .. " -ki  https://mms1.pnwireless.net:443/partners/messageReceiving/".. access_key .."/submitMessage -H \"Content-Type: application/json\" -X POST -d '{\"from\":\"" .. outbound_caller_id_number .."\",\"recipients\":[\"+".. to .."\"],\"text\":\"" .. body .. "\"}'";
-		
-		
-		elseif (carrier == "twilio") then
+			elseif (carrier == "peerless") then	
+				cmd = "curl -u" .. access_key .. ":" .. secret_key .. " -ki  https://mms1.pnwireless.net:443/partners/messageReceiving/".. access_key .."/submitMessage -H \"Content-Type: application/json\" -X POST -d '{\"from\":\"" .. outbound_caller_id_number .."\",\"recipients\":[\"+".. to .."\"],\"text\":\"" .. body .. "\"}'";
+			elseif (carrier == "twilio") then
 				if to:len() < 11 then
 					to = "1" .. to;
 				end
@@ -361,15 +411,6 @@
 				if outbound_caller_id_number:len() < 11 then
 					outbound_caller_id_number = "1" .. outbound_caller_id_number;
 				end
-				--Get User_name
-				sql = "SELECT default_setting_value FROM v_default_settings ";
-				sql = sql .. "where default_setting_category = 'sms' and default_setting_subcategory = '" .. carrier .. "_username' and default_setting_enabled = 'true'";
-				if (debug["sql"]) then
-					freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
-				end
-				status = dbh:query(sql, function(rows)
-					username = rows["default_setting_value"];
-				end);
 				cmd = "curl -X POST '" .. api_url .."' -H \"Content-Type:multipart/form-data\"  -F 'message=" .. body .. "' -F 'to_did=" .. to .."' -F 'from_did=" .. outbound_caller_id_number .. "' -u '".. username ..":".. access_key .."'"
 			elseif (carrier == "telnyx") then
 				if to:len() < 11 then
@@ -378,15 +419,6 @@
 				if outbound_caller_id_number:len() < 11 then
 					outbound_caller_id_number = "1" .. outbound_caller_id_number;
 				end
-				--Get delivery_status_webhook_url
-				sql = "SELECT default_setting_value FROM v_default_settings ";
-				sql = sql .. "where default_setting_category = 'sms' and default_setting_subcategory = '" .. carrier .. "_delivery_status_webhook_url' and default_setting_enabled = 'true'";
-				if (debug["sql"]) then
-					freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
-				end
-				status = dbh:query(sql, function(rows)
-					delivery_status_webhook_url = rows["default_setting_value"];
-				end);
 				cmd ="curl -X POST \"" .. api_url .."\" -H \"Content-Type: application/json\"  -H \"x-profile-secret: " .. secret_key .. "\" -d '{\"from\": \"+" .. outbound_caller_id_number .. "\", \"to\": \"+" .. to .. "\", \"body\": \"" .. body .. "\", \"delivery_status_webhook_url\": \"" .. delivery_status_webhook_url .. "\"}'";
 			end
 			if (debug["info"]) then
@@ -400,7 +432,7 @@
 			end
 		else
 			-- XML content
-			freeswitch.consoleLog("notice", "[sms] Body contains XML content, not sending\n");
+			freeswitch.consoleLog("notice", "[sms] Body contains XML content and/or is message delivery notification, not sending\n");
 		end	
 --		os.execute(cmd)
 	end
@@ -444,10 +476,10 @@
 		sql = "insert into v_sms_messages";
 		sql = sql .. "(sms_message_uuid,extension_uuid,domain_uuid,start_stamp,from_number,to_number,message,direction,response,carrier)";
 		sql = sql .. " values (:uuid,:extension_uuid,:domain_uuid,now(),:from,:to,:body,:direction,'',:carrier)";
-		local params = {uuid = uuid(), extension_uuid = extension_uuid, domain_uuid = domain_uuid, from = from, to = to, body = urlencode(body), direction = direction, carrier = carrier }
+		local params = {uuid = uuid(), extension_uuid = extension_uuid, domain_uuid = domain_uuid, from = from, to = to, body = savebody, direction = direction, carrier = carrier }
 
 		if (debug["sql"]) then
 			freeswitch.consoleLog("notice", "[sms] SQL: "..sql.."; params:" .. json.encode(params) .. "\n");
 		end
 		dbh:query(sql,params);
-	end
+	end

+ 14 - 0
sms/resources/templates/conf/chatplan/default.xml

@@ -15,6 +15,20 @@
                                     <action application="lua" data="app.lua sms outbound"/>
                         </condition>
                 </extension>
+		<extension name="unreg">
+  			<condition field="${sofia_contact(profile/${to})}" expression="error\/user_not_registered">
+                                <action application="set" data="final_delivery=true"/>
+                        </condition> 
+		</extension>
+<!--
+                <extension name="allowed-local">
+			<condition field="from_host" expression="<replace with allowed domain>" >
+                                <action application="set" data="final_delivery=true"/>
+                                <action application="info"/>
+                                <action application="send"/>
+                        </condition> 
+                </extension>
+-->
                 <extension name="other">
                         <condition field="to" expression="^(.*)$">
                                 <action application="set" data="final_delivery=true"/>

+ 51 - 16
sms/sms_edit.php

@@ -63,7 +63,9 @@ else {
 			$enabled = check_str($row["enabled"]);
 			$sms_destination_uuid = $row['sms_destination_uuid'];
 			$chatplan_detail_data = $row['chatplan_detail_data'];
+			$email = $row['email'];
 		}
+		unset ($prep_statement);
 	}
 	else {
 		$action = "add";
@@ -78,6 +80,7 @@ else {
 			$enabled = check_str($_POST["enabled"]);
 			$sms_destination_uuid = uuid();
 			$chatplan_detail_data = check_str($_POST["chatplan_detail_data"]);
+			$email = check_str($_POST["email"]);
 		if ($action == "add") {
 			$sql_insert = "insert into v_sms_destinations ";
 			$sql_insert .= "(";
@@ -87,19 +90,28 @@ else {
 			$sql_insert .= "destination, ";
 			$sql_insert .= "enabled, ";
 			$sql_insert .= "description, ";
-			$sql_insert .= "chatplan_detail_data ";
+			$sql_insert .= "chatplan_detail_data, ";
+			$sql_insert .= "email ";
 			$sql_insert .= ")";
 			$sql_insert .= "values ";
 			$sql_insert .= "(";
-			$sql_insert .= "'".$sms_destination_uuid."', ";
-			$sql_insert .= "'".$carrier."', ";
-			$sql_insert .= "'".$_SESSION['domain_uuid']."', ";
-			$sql_insert .= "'".$destination."', ";
-			$sql_insert .= "'".$enabled."', ";
-			$sql_insert .= "'".$description."', ";
-			$sql_insert .= "'".$chatplan_detail_data."' ";
+			$sql_insert .= ":sms_destination_uuid, ";
+			$sql_insert .= ":carrier, ";
+			$sql_insert .= ":domain_uuid, ";
+			$sql_insert .= ":destination, ";
+			$sql_insert .= ":enabled, ";
+			$sql_insert .= ":description, ";
+			$sql_insert .= ":chatplan_detail_data, ";
+			$sql_insert .= ":email ";
 			$sql_insert .= ")";
-			$db->exec($sql_insert);
+
+			$prep_statement = $db->prepare(check_sql($sql_insert));
+			$prep_statement->execute(array(':sms_destination_uuid' => $sms_destination_uuid, ':carrier' => $carrier,
+				'domain_uuid' => $_SESSION['domain_uuid'], ':destination' => $destination, ':enabled' => $enabled,
+				':description' => $description, ':chatplan_detail_data' => $chatplan_detail_data, ':email' => $email));
+			$prep_statement->execute();
+			unset ($prep_statement);
+
 			header( 'Location: sms.php') ;
 
 		}
@@ -109,17 +121,29 @@ else {
 			$description = check_str($_POST["description"]);
 			$enabled = check_str($_POST["enabled"]);
 			$chatplan_detail_data = check_str($_POST["chatplan_detail_data"]);
+			$email = check_str($_POST["email"]);
+
 
 			$sql_insert = "update v_sms_destinations set";
 			$sql_insert .= " ";
-			$sql_insert .= "carrier = '".$carrier."', ";
-			$sql_insert .= "destination = '".$destination."', ";
-			$sql_insert .= "enabled = '".$enabled."', ";
-			$sql_insert .= "description = '".$description."', ";
-			$sql_insert .= "chatplan_detail_data = '".$chatplan_detail_data."' ";
-			$sql_insert .= "where sms_destination_uuid = '" . $sms_destination_uuid . "' and domain_uuid = '" . $_SESSION['domain_uuid'] . "'";
-			$db->exec($sql_insert);
+			$sql_insert .= "carrier = :carrier, ";
+			$sql_insert .= "destination = :destination, ";
+			$sql_insert .= "enabled = :enabled, ";
+			$sql_insert .= "description = :description, ";
+			$sql_insert .= "chatplan_detail_data = :chatplan_detail_data, ";
+			$sql_insert .= "email = :email ";
+			$sql_insert .= "where sms_destination_uuid = :sms_destination_uuid and domain_uuid = :domain_uuid";
+
+
+			$prep_statement = $db->prepare(check_sql($sql_insert));
+			$prep_statement->execute(array(':carrier' => $carrier, ':destination' => $destination, ':enabled' => $enabled,
+				':description' => $description, ':chatplan_detail_data' => $chatplan_detail_data, ':email' => $email,
+				':sms_destination_uuid' => $sms_destination_uuid, ':domain_uuid' => $_SESSION['domain_uuid']));
+
+			$prep_statement->execute();
+
 			error_log($sql_insert);
+			unset ($prep_statement);
 			header( 'Location: sms.php') ;
 	}
 
@@ -189,6 +213,17 @@ else {
 	echo "</td>\n";
 	echo "</tr>\n";
 
+	echo "<tr>\n";
+	echo "<td class='vncell' valign='top' align='left' nowrap='nowrap'>\n";
+	echo "    ".$text['label-sms_email']."\n";
+	echo "</td>\n";
+	echo "<td class='vtable' align='left'>\n";
+	echo "    <input class='formfld' type='text' name='email' autocomplete='off' maxlength='255' value=\"$email\" >\n";
+	echo "<br />\n";
+	echo $text['description-sms_email']."\n";
+	echo "</td>\n";
+	echo "</tr>\n";
+
 	if (permission_exists('sms_enabled')) {
 		echo "<tr>\n";
 		echo "<td class='vncellreq' valign='top' align='left' nowrap='nowrap'>\n";

+ 172 - 0
sms/sms_email.php

@@ -0,0 +1,172 @@
+<?php
+/*
+	FusionPBX
+	Version: MPL 1.1
+
+	The contents of this file are subject to the Mozilla Public License Version
+	1.1 (the "License"); you may not use this file except in compliance with
+	the License. You may obtain a copy of the License at
+	http://www.mozilla.org/MPL/
+
+	Software distributed under the License is distributed on an "AS IS" basis,
+	WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+	for the specific language governing rights and limitations under the
+	License.
+
+	The Original Code is FusionPBX
+
+	The Initial Developer of the Original Code is
+	Mark J Crane <[email protected]>
+	Portions created by the Initial Developer are Copyright (C) 2008-2012
+	the Initial Developer. All Rights Reserved.
+
+	Contributor(s):
+	Mark J Crane <[email protected]>
+	Jonathan Black <[email protected]>
+
+*/
+
+//Regex from https://emailregex.com
+function validateEMAIL($EMAIL) {
+    $v = '/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD';
+
+    return (bool)preg_match($v, $EMAIL);
+}
+
+function send_sms_to_email($from, $to, $body, $media = "") {
+	global $db, $debug, $domain_uuid, $domain_name, $carrier;
+	if ($debug) {
+		error_log('Media: ' .  print_r($media, true));
+	}
+	//$domain_name = string.match(to,'%@+(.+)');
+	if (preg_match('/@+(.+)/',$to,$matches)) {
+		$domain_name = $matches[1];
+	}
+	//get email address from db
+	// Check for email address in sms_destinations table
+	$sql = "select domain_name, ";
+	$sql .= "email, ";
+	$sql .= "v_sms_destinations.domain_uuid as domain_uuid, ";
+	$sql .= "carrier ";
+	$sql .= "from v_sms_destinations, ";
+	$sql .= "v_domains ";
+	$sql .= "where v_sms_destinations.domain_uuid = v_domains.domain_uuid";
+	$sql .= " and destination like :to";
+//	$sql .= " and chatplan_detail_data <> ''"; //uncomment to disable email-only
+
+	if ($debug) {
+		error_log("SQL: " . print_r($sql,true));
+	}
+
+	$prep_statement = $db->prepare(check_sql($sql));
+	$prep_statement->bindValue(':to', $to);
+	$prep_statement->execute();
+	$result = $prep_statement->fetchAll(PDO::FETCH_NAMED);
+
+	if (count($result) > 0) {
+		foreach ($result as &$row) {
+			$domain_name = $row["domain_name"];
+			$email_to = $row["email"];
+			$domain_uuid = $row["domain_uuid"];
+			$carrier = $row["carrier"];
+			break; //limit to 1 row
+		}
+	}
+
+	//error_log('to: ' .  $to);
+	//error_log($email_to);
+
+	if (empty($email_to)) {
+			error_log("[sms] email address is empty, cannot send sms to email.");
+			return false;
+	}
+	else {
+
+		//set email values
+		$email_subject = 'Text Message from: ' . $from;
+		$semi_rand = md5(time());
+		$mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";
+
+		if (!empty($_SESSION['email']['smtp_from']['var']) and validateEMAIL($_SESSION['email']['smtp_from']['var'])) {
+			$headers = "From: " . $_SESSION['email']['smtp_from']['var'] . "\n";
+		}
+		else {
+			$headers = "From: [email protected]\n";
+		}
+		if ($debug) {
+			error_log("Email Sender: " . $headers);
+		}
+		$headers .= "MIME-Version: 1.0\n" . "Content-Type: multipart/mixed; " . "boundary=\"{$mime_boundary}\"";
+		$body = urldecode($body);
+		$body = preg_replace('([\n])', '<br>', $body); // fix newlines
+		$email_txt = 'To: ' . $to . '<br>Msg: ' . $body;
+
+		$email_message = "This is a multi-part message in MIME format.\n\n" .
+			"--{$mime_boundary}\n" . "Content-Type:text/html; charset = \"iso-8859-1\"\n" .
+			"Content-Transfer-Encoding: 7bit\n\n" .	$email_txt . "\n\n";
+
+		if ($carrier == "telnyx") {
+			if (gettype($media)=="array") {
+				$email_txt = 'To: ' . $to . '<br>Msg: ' . $body . '<br>MMS Message received, see attachment';
+
+				$email_message = "This is a multi-part message in MIME format.\n\n" .
+					"--{$mime_boundary}\n" . "Content-Type:text/html; charset = \"iso-8859-1\"\n" .
+					"Content-Transfer-Encoding: 7bit\n\n" .	$email_txt . "\n\n";
+				//process MMS attachment
+				foreach ($media as $attachment) {
+					$url = $attachment->url;
+					$start = strrpos($url, '/') == -1 ? strrpos($url, '//') : strrpos($url, '/')+1;
+					$fileatt_name = substr($url, $start, strlen($url)); // Filename that will be used for the file as the attachment
+					if (!empty($_SESSION['sms']['mms_attachment_temp_path']['text'])) {
+						$fileatt = $_SESSION['sms']['mms_attachment_temp_path']['text'];
+						if (substr($fileatt, -1) != '/') {
+							$fileatt .= '/';
+						}
+						$fileatt .= $fileatt_name;
+					}
+					else {
+						$fileatt = '/var/www/fusionpbx/app/sms/tmp/' . $fileatt_name; // Path to the file
+					}
+
+					// download attachment
+					file_put_contents($fileatt, fopen($url, 'r'));
+
+					//$fileatt_type = "application/octet-stream"; // File Type
+					$fileatt_type = $attachment->content_type; // File Type
+
+					//$start = strrpos($attachment, '/') == -1 ? strrpos($attachment, '//') : strrpos($attachment, '/')+1;
+
+					$file = fopen($fileatt,'rb');
+					$attdata = fread($file,filesize($fileatt));
+					fclose($file);
+
+
+					$attdata = chunk_split(base64_encode($attdata));
+
+					$email_message .= "--{$mime_boundary}\n" . "Content-Type: {$fileatt_type};\n" .
+						" name = \"{$fileatt_name}\"\n" . "Content-Disposition: inline;\n" . " filename = \"{$fileatt_name}\"\n" .
+						"Content-Transfer-Encoding:base64\n\n" . $attdata . "\n\n" . "--{$mime_boundary}--\n";
+					error_log("email_message: " . $email_message);
+					unlink($fileatt); // delete a file after attachment sent.
+				}
+			}
+		}
+		else {
+			$email_message .= "--{$mime_boundary}--\n";
+		}
+		if ($debug) {
+			error_log("headers: " . $headers);
+		}
+		//send email
+		$ok = mail($email_to, $email_subject, $email_message, $headers);
+
+		if($ok) {
+			error_log("[sms] Email Sent Successfully.");
+			return true;
+		}else {
+			error_log("[sms] Email could not be sent.");
+			return false;
+		}
+	}
+}
+?>

+ 45 - 14
sms/sms_hook_common.php

@@ -31,6 +31,7 @@
 
 */
 include "root.php";
+include "app/sms/sms_email.php";
 
 //luarun /var/www/html/app/sms/sms.lua TO FROM 'BODY'
 
@@ -38,20 +39,17 @@ $debug = true;
 
 require_once "resources/require.php";
 
-function route_and_send_sms($from, $to, $body) {
-	global $db, $debug, $domain_uuid, $domain_name;
-	if ($debug) {
-		error_log('DATA: ' .  print_r($to, true));
-	}
+function route_and_send_sms($from, $to, $body, $media = "") {
+	global $db, $debug, $domain_uuid, $domain_name, $mailsent;
 
-	//create the even socket connection and send the event socket command
+	//create the event socket connection and send the event socket command
 		$fp = event_socket_create($_SESSION['event_socket_ip_address'], $_SESSION['event_socket_port'], $_SESSION['event_socket_password']);
 		if (!$fp) {
 			//error message
 			echo "<div align='center'><strong>Connection to Event Socket failed.</strong></div>";
 		}
 		else {
-
+				$mailsent = false;
 				$to = intval(preg_replace('/(^[1])/','', $to));
 				$from = intval($from);
 				$body = preg_replace('([\'])', '\\\'', $body); // escape apostrophes
@@ -60,6 +58,25 @@ function route_and_send_sms($from, $to, $body) {
 					error_log("FROM: " . print_r($from,true));
 					error_log("BODY: " . print_r($body,true));
 				}
+				$mailbody = $body;
+				if (gettype($media)=="array") {
+					if (empty($body)) {
+						$body = "MMS message received, see email for attachment";
+					}
+					else {
+						$body .= " (MMS message received, see email for attachment)";
+					}
+					if ($debug) {
+						error_log("MMS message (media array present)");
+					}
+				}
+				if ($debug) {
+					error_log("BODY: " . print_r($body,true));
+				}
+				$body = preg_replace('([\n])', '<br>', $body); // escape newlines
+				if ($debug) {
+					error_log("BODY-revised: " . print_r($body,true));
+				}
 
 				// Check for chatplan_detail in sms_destinations table
 				$sql = "select domain_name, ";
@@ -127,7 +144,17 @@ function route_and_send_sms($from, $to, $body) {
 					error_log("DOMAIN_UUID: " . print_r($domain_uuid,true));
 
 				}
+				//load default and domain settings
+				$_SESSION["domain_uuid"] = $domain_uuid;
+				require_once "resources/classes/domains.php";
+				$domain = new domains;
+				$domain->set();
+				if ($debug) {
+					error_log("email_from: " . $_SESSION['email']['smtp_from']['var']);
+				}
+				$mailsent = send_sms_to_email($from, $to, $mailbody, $media);
 
+				//check to see if we have a ring group or single extension
 				$sql = "select destination_number ";
 				$sql .= "from v_ring_groups, v_ring_group_destinations ";
 				$sql .= "where v_ring_groups.ring_group_uuid = v_ring_group_destinations.ring_group_uuid ";
@@ -141,25 +168,29 @@ function route_and_send_sms($from, $to, $body) {
 					error_log("RG RESULT: " . print_r($result,true));
 				}
 
-				if (count($result)) {
+				//send sms via Lua script
+				if (count($result)) { //ring group
 					foreach ($result as &$row) {
 						$switch_cmd = "api luarun app.lua sms inbound ";
 						$switch_cmd .= $row['destination_number'] . "@" . $domain_name;
-						$switch_cmd .= " " . $from . " '" . $body . "'";
+						$switch_cmd .= " " . $from . " '" . $body . "' " . $mailsent;
 						if ($debug) {
 							error_log(print_r($switch_cmd,true));
 						}
 						$result2 = trim(event_socket_request($fp, $switch_cmd));
+						if ($debug) {
+							error_log("RESULT: " . print_r($result2,true));
+						}
 					}
-				} else {
-					$switch_cmd = "api luarun app.lua sms inbound " . $match[0] . "@" . $domain_name . " " . $from . " '" . $body . "'";
+				} else { //single extension
+					$switch_cmd = "api luarun app.lua sms inbound " . $match[0] . "@" . $domain_name . " " . $from . " '" . $body . "' " . $mailsent;
 					if ($debug) {
 						error_log(print_r($switch_cmd,true));
 					}
 					$result2 = trim(event_socket_request($fp, $switch_cmd));
-				}
-				if ($debug) {
-					error_log("RESULT: " . print_r($result2,true));
+					if ($debug) {
+						error_log("RESULT: " . print_r($result2,true));
+					}
 				}
 
 				unset ($prep_statement);

+ 6 - 1
sms/sms_mdr.php

@@ -125,7 +125,12 @@ else {
 			echo "	<td valign='top' class='".$row_style[$c]."'>".$row['start_stamp']."&nbsp;</td>\n";
 			echo "	<td valign='top' class='".$row_style[$c]."'>".$row['from_number']."&nbsp;</td>\n";
 			echo "	<td valign='top' class='".$row_style[$c]."'>".$row['to_number']."&nbsp;</td>\n";
-			echo "	<td valign='top' class='".$row_style[$c]."'>".urldecode($row['message'])."&nbsp;</td>\n";
+			$tmpmsg = urldecode($row['message']);
+			//$tmpmsg=htmlspecialcharacters($tmpmsg);
+			$tmpmsg = str_replace('>', '&gt;', $tmpmsg);
+			$tmpmsg = str_replace('<', '&lt;', $tmpmsg);
+			echo "	<td valign='top' class='".$row_style[$c]."'>".$tmpmsg."&nbsp;</td>\n";
+			//echo "	<td valign='top' class='".$row_style[$c]."'>".$row['message']."&nbsp;</td>\n";
 			echo "</tr>\n";
 			$c = ($c) ? 0 : 1;
 		} // end foreach