From 43509669cb267d328d610bd5f798d382c81b15a7 Mon Sep 17 00:00:00 2001 From: maggikpunkt Date: Tue, 17 Mar 2015 16:33:44 +0100 Subject: [PATCH 01/21] Setting NLS_DATE_LANGUAGE for date conversion The date conversions fail if the the language is set to something different than english. --- ora/rss_util_pkg.pkb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ora/rss_util_pkg.pkb b/ora/rss_util_pkg.pkb index b819d94..084eb84 100755 --- a/ora/rss_util_pkg.pkb +++ b/ora/rss_util_pkg.pkb @@ -39,7 +39,7 @@ begin */ if p_format = g_format_rss then - l_returnvalue := to_char(p_date, g_date_format_rss); + l_returnvalue := to_char(p_date, g_date_format_rss,'NLS_DATE_LANGUAGE = ENGLISH'); elsif p_format = g_format_rdf then l_returnvalue := to_char(p_date, g_date_format_rdf); elsif p_format = g_format_atom then @@ -75,7 +75,7 @@ begin begin if p_format = g_format_rss then --l_returnvalue := to_date(p_date_str, g_date_format_rss); - l_returnvalue := to_date(substr(p_date_str,1,26), 'Dy, DD Mon YYYY HH24:MI:SS'); + l_returnvalue := to_date(substr(p_date_str,1,26), 'Dy, DD Mon YYYY HH24:MI:SS','NLS_DATE_LANGUAGE = ENGLISH'); elsif p_format = g_format_rdf then --l_returnvalue := to_date(p_date_str, g_date_format_rdf); l_returnvalue := to_date(substr(p_date_str,1,19), 'YYYY-MM-DD"T"HH24:MI:SS'); From 9c8dceb9854c27407980518cb366d9c4348674a9 Mon Sep 17 00:00:00 2001 From: Eric Olson Date: Fri, 20 Mar 2015 18:46:33 -0500 Subject: [PATCH 02/21] Create ntlm_http_pkg.get_response_blob Added a function to ntlm_http_pkg to retrieve the URL as a BLOB. --- ora/ntlm_http_pkg.pkb | 224 ++++++++++++++++++++++++++++++++++++++++++ ora/ntlm_http_pkg.pks | 8 ++ 2 files changed, 232 insertions(+) diff --git a/ora/ntlm_http_pkg.pkb b/ora/ntlm_http_pkg.pkb index bdf93b4..cee54bd 100755 --- a/ora/ntlm_http_pkg.pkb +++ b/ora/ntlm_http_pkg.pkb @@ -51,6 +51,230 @@ begin end headers_contain_value; +function get_response_blob (p_url in varchar2, + p_username in varchar2, + p_password in varchar2, + p_wallet_path in varchar2 := null, + p_wallet_password in varchar2 := null, + p_proxy_server in varchar2 := null) return blob +as + + l_req utl_http.req; + l_resp utl_http.resp; + l_returnvalue blob; + + l_authenticate_with_ntlm boolean; + + l_name varchar2(500); + l_value varchar2(500); + + l_ntlm_message varchar2(500); + + l_negotiate_message varchar2(500); + + l_server_challenge raw(4000); + l_negotiate_flags raw(4000); + + l_authenticate_message varchar2(500); + + + function get_response_body (p_resp in out utl_http.resp) return blob + as + l_data raw(32767); + l_returnvalue blob; + begin + + /* + + Purpose: get the response body as a blob + + Remarks: + + Who Date Description + ------ ---------- -------------------------------- + MBR 24.06.2011 Created + + */ + + dbms_lob.createtemporary( l_returnvalue, true ); + + begin + loop + utl_http.read_raw(r => p_resp, data => l_data); + dbms_lob.append( l_returnvalue, to_blob( l_data )); + end loop; + exception + when utl_http.end_of_body then + null; + end; + + return l_returnvalue; + + end get_response_body; + + + procedure debug_response (p_resp in out utl_http.resp) + as + l_name varchar2(255); + l_value varchar2(2000); + l_body blob; + begin + + /* + + Purpose: print debug info about the response + + Remarks: + + Who Date Description + ------ ---------- -------------------------------- + MBR 24.06.2011 Created + + */ + + debug_pkg.printf('Response Status Code: %1', p_resp.status_code); + + for i in 1 .. utl_http.get_header_count (p_resp) loop + utl_http.get_header (p_resp, i, l_name, l_value); + debug_pkg.printf('#%1 %2 : %3', i, l_name, l_value); + end loop; + + l_returnvalue := get_response_body (p_resp); + + debug_pkg.printf('Body length = %1', dbms_lob.getlength (l_returnvalue)); + debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count); + + end debug_response; + + +begin + + /* + + Purpose: Get response clob from URL + + Remarks: see http://davenport.sourceforge.net/ntlm.html#ntlmHttpAuthentication + + Who Date Description + ------ ---------- -------------------------------- + FDL 11.05.2011 Created + MBR 03.06.2011 Lots of changes + + */ + + utl_http.set_detailed_excp_support (enable => true); + utl_http.set_response_error_check (false); + + utl_http.set_persistent_conn_support (true, 10); + + debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count); + + -- support for HTTPS + if instr(lower(p_url),'https') = 1 then + utl_http.set_wallet (p_wallet_path, p_wallet_password); + end if; + + -- support for proxy server + if p_proxy_server is not null then + utl_http.set_proxy (p_proxy_server); + end if; + + ------------ + -- Request 1 + ------------ + + debug_pkg.printf(' '); + debug_pkg.printf(p_url); + + l_req := utl_http.begin_request(p_url); + + l_resp := utl_http.get_response (l_req); + + debug_response (l_resp); + + if l_resp.status_code = utl_http.HTTP_UNAUTHORIZED then + + l_authenticate_with_ntlm := headers_contain_value (l_resp, 'WWW-Authenticate', 'NTLM'); + + utl_http.end_response (l_resp); + + if l_authenticate_with_ntlm then + + l_negotiate_message := 'NTLM ' || ntlm_util_pkg.get_negotiate_message(p_username); + -- need to send negotiation message + + debug_pkg.printf('Negotiate message: %1', l_negotiate_message); + + ------------ + -- Request 2 + ------------ + + debug_pkg.printf(' '); + debug_pkg.printf(p_url); + l_req := utl_http.begin_request(p_url); + utl_http.set_header (l_req, 'Authorization', l_negotiate_message); + + l_resp := utl_http.get_response(l_req); + + debug_response (l_resp); + + if l_resp.status_code = utl_http.HTTP_UNAUTHORIZED then + + -- received server challenge + utl_http.get_header_by_name(l_resp, 'WWW-Authenticate', l_value, 1); + + utl_http.end_response(l_resp); + + if substr(l_value, 1, 4) = 'NTLM' then + + -- get value + l_value := substr(l_value, 6); + + ntlm_util_pkg.parse_challenge_message (l_value, l_server_challenge, l_negotiate_flags); + + l_authenticate_message := 'NTLM ' || ntlm_util_pkg.get_authenticate_message(p_username, p_password, l_server_challenge, l_negotiate_flags); + debug_pkg.printf('Authenticate message: "%1"', l_authenticate_message); + + ------------ + -- Request 3 + ------------ + + -- sending NTLM message 3 + debug_pkg.printf(' '); + debug_pkg.printf(p_url); + l_req := utl_http.begin_request(p_url); + + utl_http.set_header (l_req, 'Connection', 'close'); + utl_http.set_header (l_req, 'Authorization', l_authenticate_message); + + l_resp := utl_http.get_response (l_req); + + debug_response (l_resp); + + -- this is already done inside debug_response + --l_returnvalue := get_response_body (l_resp); + + utl_http.end_response(l_resp); + + end if; + + end if; + + else + debug_pkg.printf('Server is not configured with NTLM security (missing "WWW-Authenticate: NTLM" header).'); + end if; + + end if; + + + utl_http.close_persistent_conns; + debug_pkg.printf('Persistent connection count (should be zero): %1', utl_http.get_persistent_conn_count); + + return l_returnvalue; + +end get_response_blob; + + function get_response_clob (p_url in varchar2, p_username in varchar2, p_password in varchar2, diff --git a/ora/ntlm_http_pkg.pks b/ora/ntlm_http_pkg.pks index 9625ea1..1cfe91f 100755 --- a/ora/ntlm_http_pkg.pks +++ b/ora/ntlm_http_pkg.pks @@ -17,6 +17,14 @@ as */ + -- get blob from url + function get_response_blob (p_url in varchar2, + p_username in varchar2, + p_password in varchar2, + p_wallet_path in varchar2 := null, + p_wallet_password in varchar2 := null, + p_proxy_server in varchar2 := null) return blob; + -- get clob from url function get_response_clob (p_url in varchar2, p_username in varchar2, From de4443fd8c0b97b14217eeacceaad46eead34069 Mon Sep 17 00:00:00 2001 From: Trent Schafer Date: Sat, 21 Mar 2015 12:05:32 +1100 Subject: [PATCH 03/21] Dynamically determine UTC date string --- ora/amazon_aws_auth_pkg.pkb | 35 ++++++----------------------------- ora/amazon_aws_auth_pkg.pks | 6 +----- 2 files changed, 7 insertions(+), 34 deletions(-) diff --git a/ora/amazon_aws_auth_pkg.pkb b/ora/amazon_aws_auth_pkg.pkb index 94345a9..c624264 100755 --- a/ora/amazon_aws_auth_pkg.pkb +++ b/ora/amazon_aws_auth_pkg.pkb @@ -17,9 +17,6 @@ as g_aws_id varchar2(20) := 'my_aws_id'; -- AWS access key ID g_aws_key varchar2(40) := 'my_aws_key'; -- AWS secret key - g_gmt_offset number := 0; -- your timezone GMT adjustment - - function get_auth_string (p_string in varchar2) return varchar2 as l_returnvalue varchar2(32000); @@ -100,6 +97,8 @@ end get_aws_id; function get_date_string (p_date in date := sysdate) return varchar2 as l_returnvalue varchar2(255); + l_date_as_time timestamp(6); + l_time_utc timestamp(6); begin /* @@ -114,7 +113,9 @@ begin */ - l_returnvalue := to_char(p_date + g_gmt_offset/24, 'Dy, DD Mon YYYY HH24:MI:SS', 'NLS_DATE_LANGUAGE = AMERICAN') || ' GMT'; + l_date_as_time := cast(p_date as timestamp); + l_time_utc := sys_extract_utc(l_date_as_time); + l_returnvalue := to_char(l_time_utc, 'Dy, DD Mon YYYY HH24:MI:SS', 'NLS_DATE_LANGUAGE = AMERICAN') || ' GMT'; return l_returnvalue; @@ -187,31 +188,8 @@ begin end set_aws_key; - -procedure set_gmt_offset (p_gmt_offset in number) -as -begin - - /* - - Purpose: set GMT offset - - Remarks: - - Who Date Description - ------ ---------- ------------------------------------- - MBR 03.03.2011 Created - - */ - - g_gmt_offset := p_gmt_offset; - -end set_gmt_offset; - - procedure init (p_aws_id in varchar2, - p_aws_key in varchar2, - p_gmt_offset in number) + p_aws_key in varchar2) as begin @@ -229,7 +207,6 @@ begin g_aws_id := p_aws_id; g_aws_key := p_aws_key; - g_gmt_offset := nvl(p_gmt_offset, g_gmt_offset); end init; diff --git a/ora/amazon_aws_auth_pkg.pks b/ora/amazon_aws_auth_pkg.pks index f2e14f4..3f24ff2 100755 --- a/ora/amazon_aws_auth_pkg.pks +++ b/ora/amazon_aws_auth_pkg.pks @@ -37,13 +37,9 @@ as -- set AWS secret key procedure set_aws_key (p_aws_key in varchar2); - -- set GMT offset - procedure set_gmt_offset (p_gmt_offset in number); - -- initialize package for use procedure init (p_aws_id in varchar2, - p_aws_key in varchar2, - p_gmt_offset in number := null); + p_aws_key in varchar2); end amazon_aws_auth_pkg; / From a6b5515e4dfb1d123279cbca50591d86860edfe6 Mon Sep 17 00:00:00 2001 From: Trent Schafer Date: Sun, 22 Mar 2015 22:34:03 +1100 Subject: [PATCH 04/21] Restore g_gmt_offset for backward compatibility --- ora/amazon_aws_auth_pkg.pkb | 106 +++++++++++++++++++++++------------- ora/amazon_aws_auth_pkg.pks | 23 ++++---- 2 files changed, 81 insertions(+), 48 deletions(-) diff --git a/ora/amazon_aws_auth_pkg.pkb b/ora/amazon_aws_auth_pkg.pkb index c624264..75be024 100755 --- a/ora/amazon_aws_auth_pkg.pkb +++ b/ora/amazon_aws_auth_pkg.pkb @@ -11,41 +11,44 @@ as Who Date Description ------ ---------- ------------------------------------- MBR 09.01.2011 Created - + */ g_aws_id varchar2(20) := 'my_aws_id'; -- AWS access key ID g_aws_key varchar2(40) := 'my_aws_key'; -- AWS secret key + g_gmt_offset number := NULL; -- your timezone GMT adjustment + + function get_auth_string (p_string in varchar2) return varchar2 as l_returnvalue varchar2(32000); - l_encrypted_raw raw (2000); -- stores encrypted binary text - l_decrypted_raw raw (2000); -- stores decrypted binary text - l_key_bytes_raw raw (64); -- stores 256-bit encryption key + l_encrypted_raw raw (2000); -- stores encrypted binary text + l_decrypted_raw raw (2000); -- stores decrypted binary text + l_key_bytes_raw raw (64); -- stores 256-bit encryption key begin /* - Purpose: get authentication string + Purpose: get authentication string Remarks: see http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html#ConstructingTheAuthenticationHeader Who Date Description ------ ---------- ------------------------------------- MBR 09.01.2011 Created - + */ - l_key_bytes_raw := utl_i18n.string_to_raw (g_aws_key, 'AL32UTF8'); - l_decrypted_raw := utl_i18n.string_to_raw (p_string, 'AL32UTF8'); - + l_key_bytes_raw := utl_i18n.string_to_raw (g_aws_key, 'AL32UTF8'); + l_decrypted_raw := utl_i18n.string_to_raw (p_string, 'AL32UTF8'); + l_encrypted_raw := dbms_crypto.mac (src => l_decrypted_raw, typ => dbms_crypto.hmac_sh1, key => l_key_bytes_raw); - - l_returnvalue := utl_i18n.raw_to_char (utl_encode.base64_encode(l_encrypted_raw), 'AL32UTF8'); - + + l_returnvalue := utl_i18n.raw_to_char (utl_encode.base64_encode(l_encrypted_raw), 'AL32UTF8'); + l_returnvalue := 'AWS ' || g_aws_id || ':' || l_returnvalue; - + return l_returnvalue; end get_auth_string; @@ -53,22 +56,22 @@ end get_auth_string; function get_signature (p_string in varchar2) return varchar2 as - + begin /* Purpose: get signature part of authentication string - Remarks: + Remarks: Who Date Description ------ ---------- ------------------------------------- MBR 09.01.2011 Created - + */ - - return substr(get_auth_string(p_string),26); + + return substr(get_auth_string(p_string),26); end get_signature; @@ -81,12 +84,12 @@ begin Purpose: get AWS access key ID - Remarks: + Remarks: Who Date Description ------ ---------- ------------------------------------- MBR 09.01.2011 Created - + */ return g_aws_id; @@ -105,17 +108,21 @@ begin Purpose: get AWS access key ID - Remarks: + Remarks: Who Date Description ------ ---------- ------------------------------------- MBR 09.01.2011 Created - + */ - - l_date_as_time := cast(p_date as timestamp); - l_time_utc := sys_extract_utc(l_date_as_time); - l_returnvalue := to_char(l_time_utc, 'Dy, DD Mon YYYY HH24:MI:SS', 'NLS_DATE_LANGUAGE = AMERICAN') || ' GMT'; + + if g_gmt_offset is null then + l_date_as_time := cast(p_date as timestamp); + l_time_utc := sys_extract_utc(l_date_as_time); + l_returnvalue := to_char(l_time_utc, 'Dy, DD Mon YYYY HH24:MI:SS', 'NLS_DATE_LANGUAGE = AMERICAN') || ' GMT'; + else + l_returnvalue := to_char(p_date + g_gmt_offset/24, 'Dy, DD Mon YYYY HH24:MI:SS', 'NLS_DATE_LANGUAGE = AMERICAN') || ' GMT'; + end if; return l_returnvalue; @@ -131,12 +138,12 @@ begin Purpose: get epoch (number of seconds since January 1, 1970) - Remarks: + Remarks: Who Date Description ------ ---------- ------------------------------------- MBR 09.01.2011 Created - + */ l_returnvalue := trunc((p_date - to_date('01-01-1970','MM-DD-YYYY')) * 24 * 60 * 60); @@ -154,19 +161,19 @@ begin Purpose: set AWS access key id - Remarks: + Remarks: Who Date Description ------ ---------- ------------------------------------- MBR 18.01.2011 Created - + */ - + g_aws_id := p_aws_id; end set_aws_id; - + procedure set_aws_key (p_aws_key in varchar2) as @@ -176,20 +183,43 @@ begin Purpose: set AWS secret key - Remarks: + Remarks: Who Date Description ------ ---------- ------------------------------------- MBR 18.01.2011 Created - + */ g_aws_key := p_aws_key; end set_aws_key; + +procedure set_gmt_offset (p_gmt_offset in number) +as +begin + + /* + + Purpose: set GMT offset + + Remarks: + + Who Date Description + ------ ---------- ------------------------------------- + MBR 03.03.2011 Created + + */ + + g_gmt_offset := p_gmt_offset; + +end set_gmt_offset; + + procedure init (p_aws_id in varchar2, - p_aws_key in varchar2) + p_aws_key in varchar2, + p_gmt_offset in number := NULL) as begin @@ -197,19 +227,19 @@ begin Purpose: initialize package for use - Remarks: + Remarks: Who Date Description ------ ---------- ------------------------------------- MBR 03.03.2011 Created - + */ g_aws_id := p_aws_id; g_aws_key := p_aws_key; + g_gmt_offset := p_gmt_offset; end init; end amazon_aws_auth_pkg; / - diff --git a/ora/amazon_aws_auth_pkg.pks b/ora/amazon_aws_auth_pkg.pks index 3f24ff2..7769d57 100755 --- a/ora/amazon_aws_auth_pkg.pks +++ b/ora/amazon_aws_auth_pkg.pks @@ -7,15 +7,15 @@ as Remarks: inspired by the whitepaper "Building an Amazon S3 Client with Application Express 4.0" by Jason Straub see http://jastraub.blogspot.com/2011/01/building-amazon-s3-client-with.html - - dependencies: owner of this package needs execute on dbms_crypto + + dependencies: owner of this package needs execute on dbms_crypto Who Date Description ------ ---------- ------------------------------------- MBR 09.01.2011 Created - + */ - + -- get "Authorization" (actually authentication) header string function get_auth_string (p_string in varchar2) return varchar2; @@ -24,23 +24,26 @@ as -- get AWS access key ID function get_aws_id return varchar2; - + -- get date string function get_date_string (p_date in date := sysdate) return varchar2; - + -- get epoch (number of seconds since January 1, 1970) function get_epoch (p_date in date) return number; - + -- set AWS access key id procedure set_aws_id (p_aws_id in varchar2); - + -- set AWS secret key procedure set_aws_key (p_aws_key in varchar2); + -- set GMT offset + procedure set_gmt_offset (p_gmt_offset in number); + -- initialize package for use procedure init (p_aws_id in varchar2, - p_aws_key in varchar2); + p_aws_key in varchar2, + p_gmt_offset in number := NULL); end amazon_aws_auth_pkg; / - From ebe4c7655e1ec41b329f283b6219f82e0129c605 Mon Sep 17 00:00:00 2001 From: Eric Olson Date: Mon, 6 Apr 2015 16:49:18 -0500 Subject: [PATCH 05/21] Consolidate functions to avoid duplication Broke out to avoid duplication, as requested by mortenbra. Response is now passed around internally as a record, with a flag to indicate whether the BLOB or CLOB is relevant. --- ora/ntlm_http_pkg.pkb | 380 ++++++++++++------------------------------ ora/ntlm_http_pkg.pks | 1 - 2 files changed, 111 insertions(+), 270 deletions(-) diff --git a/ora/ntlm_http_pkg.pkb b/ora/ntlm_http_pkg.pkb index cee54bd..cb5adcf 100755 --- a/ora/ntlm_http_pkg.pkb +++ b/ora/ntlm_http_pkg.pkb @@ -1,5 +1,13 @@ create or replace package body ntlm_http_pkg as + c_blob constant number(1) := 0; + c_clob constant number(1) := 1; + + type t_response_body is record ( + l_blob blob, + l_clob clob, + l_blob_or_clob number(1) default 1 + ); /* @@ -51,12 +59,55 @@ begin end headers_contain_value; -function get_response_blob (p_url in varchar2, - p_username in varchar2, - p_password in varchar2, - p_wallet_path in varchar2 := null, - p_wallet_password in varchar2 := null, - p_proxy_server in varchar2 := null) return blob +procedure get_response_body (p_resp in out utl_http.resp, p_response_body in out nocopy t_response_body) +as + l_bdata raw(32767); + l_cdata string_util_pkg.t_max_pl_varchar2; +begin + /* + + Purpose: get the response body as a blob + + Remarks: + + Who Date Description + ------ ---------- -------------------------------- + MBR 24.06.2011 Created + EAO 22.03.2015 Works with BLOBs + + */ + + if p_response_body.l_blob_or_clob = c_blob then + begin + loop + utl_http.read_raw(r => p_resp, data => l_bdata); + dbms_lob.append( p_response_body.l_blob, to_blob( l_bdata )); + end loop; + exception + when utl_http.end_of_body then + null; + end; + else + begin + loop + utl_http.read_text(r => p_resp, data => l_cdata); + p_response_body.l_clob := p_response_body.l_clob || l_cdata; + end loop; + exception + when utl_http.end_of_body then + null; + end; + end if; +end get_response_body; + + +procedure get_response (p_url in varchar2, + p_username in varchar2, + p_password in varchar2, + p_wallet_path in varchar2 := null, + p_wallet_password in varchar2 := null, + p_proxy_server in varchar2 := null, + p_response_body in out nocopy t_response_body) as l_req utl_http.req; @@ -76,62 +127,14 @@ as l_negotiate_flags raw(4000); l_authenticate_message varchar2(500); - - - function get_response_body (p_resp in out utl_http.resp) return blob - as - l_data raw(32767); - l_returnvalue blob; - begin - - /* - - Purpose: get the response body as a blob - - Remarks: - - Who Date Description - ------ ---------- -------------------------------- - MBR 24.06.2011 Created - - */ - - dbms_lob.createtemporary( l_returnvalue, true ); - - begin - loop - utl_http.read_raw(r => p_resp, data => l_data); - dbms_lob.append( l_returnvalue, to_blob( l_data )); - end loop; - exception - when utl_http.end_of_body then - null; - end; - - return l_returnvalue; - - end get_response_body; - - + procedure debug_response (p_resp in out utl_http.resp) as l_name varchar2(255); l_value varchar2(2000); - l_body blob; + l_body clob; begin - /* - - Purpose: print debug info about the response - - Remarks: - - Who Date Description - ------ ---------- -------------------------------- - MBR 24.06.2011 Created - - */ - debug_pkg.printf('Response Status Code: %1', p_resp.status_code); for i in 1 .. utl_http.get_header_count (p_resp) loop @@ -139,7 +142,7 @@ as debug_pkg.printf('#%1 %2 : %3', i, l_name, l_value); end loop; - l_returnvalue := get_response_body (p_resp); + get_response_body (p_resp, p_response_body); debug_pkg.printf('Body length = %1', dbms_lob.getlength (l_returnvalue)); debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count); @@ -148,7 +151,6 @@ as begin - /* Purpose: Get response clob from URL @@ -270,7 +272,45 @@ begin utl_http.close_persistent_conns; debug_pkg.printf('Persistent connection count (should be zero): %1', utl_http.get_persistent_conn_count); - return l_returnvalue; +end get_response; + + +function get_response_blob (p_url in varchar2, + p_username in varchar2, + p_password in varchar2, + p_wallet_path in varchar2 := null, + p_wallet_password in varchar2 := null, + p_proxy_server in varchar2 := null) return blob +as + l_response_body t_response_body; +begin + + /* + + Purpose: Get response clob from URL + + Remarks: see http://davenport.sourceforge.net/ntlm.html#ntlmHttpAuthentication + + Who Date Description + ------ ---------- -------------------------------- + FDL 11.05.2011 Created + MBR 03.06.2011 Lots of changes + EAO 22.03.2015 Most functionality moved to get_response + + */ + + dbms_lob.createtemporary( l_response_body.l_blob, true); + l_response_body.l_blob_or_clob := c_blob; + get_response( + p_url => get_response_blob.p_url, + p_username => get_response_blob.p_username, + p_password => get_response_blob.p_password, + p_wallet_path => get_response_blob.p_wallet_path, + p_wallet_password => get_response_blob.p_wallet_password, + p_proxy_server => get_response_blob.p_proxy_server, + p_response_body => l_response_body); + + return l_response_body.l_blob; end get_response_blob; @@ -282,219 +322,22 @@ function get_response_clob (p_url in varchar2, p_wallet_password in varchar2 := null, p_proxy_server in varchar2 := null) return clob as - - l_req utl_http.req; - l_resp utl_http.resp; - l_returnvalue clob; - - l_authenticate_with_ntlm boolean; - - l_name varchar2(500); - l_value varchar2(500); - - l_ntlm_message varchar2(500); - - l_negotiate_message varchar2(500); - - l_server_challenge raw(4000); - l_negotiate_flags raw(4000); - - l_authenticate_message varchar2(500); - - - function get_response_body (p_resp in out utl_http.resp) return clob - as - l_data string_util_pkg.t_max_pl_varchar2; - l_returnvalue clob; - begin - - /* - - Purpose: get the response body as a clob - - Remarks: - - Who Date Description - ------ ---------- -------------------------------- - MBR 24.06.2011 Created - - */ - - begin - loop - utl_http.read_text(r => p_resp, data => l_data); - l_returnvalue := l_returnvalue || l_data; - end loop; - exception - when utl_http.end_of_body then - null; - end; - - return l_returnvalue; - - end get_response_body; - - - procedure debug_response (p_resp in out utl_http.resp) - as - l_name varchar2(255); - l_value varchar2(2000); - l_body clob; - begin - - /* - - Purpose: print debug info about the response - - Remarks: - - Who Date Description - ------ ---------- -------------------------------- - MBR 24.06.2011 Created - - */ - - debug_pkg.printf('Response Status Code: %1', p_resp.status_code); - - for i in 1 .. utl_http.get_header_count (p_resp) loop - utl_http.get_header (p_resp, i, l_name, l_value); - debug_pkg.printf('#%1 %2 : %3', i, l_name, l_value); - end loop; - - l_returnvalue := get_response_body (p_resp); - - debug_pkg.printf('Body length = %1', dbms_lob.getlength (l_returnvalue)); - debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count); - - end debug_response; - - + l_response_body t_response_body; begin - - /* - - Purpose: Get response clob from URL - - Remarks: see http://davenport.sourceforge.net/ntlm.html#ntlmHttpAuthentication - - Who Date Description - ------ ---------- -------------------------------- - FDL 11.05.2011 Created - MBR 03.06.2011 Lots of changes - - */ + dbms_lob.createtemporary( l_response_body.l_clob, true); + l_response_body.l_blob_or_clob := c_clob; - utl_http.set_detailed_excp_support (enable => true); - utl_http.set_response_error_check (false); + get_response( + p_url => get_response_clob.p_url, + p_username => get_response_clob.p_username, + p_password => get_response_clob.p_password, + p_wallet_path => get_response_clob.p_wallet_path, + p_wallet_password => get_response_clob.p_wallet_password, + p_proxy_server => get_response_clob.p_proxy_server, + p_response_body => l_response_body); - utl_http.set_persistent_conn_support (true, 10); - - debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count); - - -- support for HTTPS - if instr(lower(p_url),'https') = 1 then - utl_http.set_wallet (p_wallet_path, p_wallet_password); - end if; + return l_response_body.l_clob; - -- support for proxy server - if p_proxy_server is not null then - utl_http.set_proxy (p_proxy_server); - end if; - - ------------ - -- Request 1 - ------------ - - debug_pkg.printf(' '); - debug_pkg.printf(p_url); - - l_req := utl_http.begin_request(p_url); - - l_resp := utl_http.get_response (l_req); - - debug_response (l_resp); - - if l_resp.status_code = utl_http.HTTP_UNAUTHORIZED then - - l_authenticate_with_ntlm := headers_contain_value (l_resp, 'WWW-Authenticate', 'NTLM'); - - utl_http.end_response (l_resp); - - if l_authenticate_with_ntlm then - - l_negotiate_message := 'NTLM ' || ntlm_util_pkg.get_negotiate_message(p_username); - -- need to send negotiation message - - debug_pkg.printf('Negotiate message: %1', l_negotiate_message); - - ------------ - -- Request 2 - ------------ - - debug_pkg.printf(' '); - debug_pkg.printf(p_url); - l_req := utl_http.begin_request(p_url); - utl_http.set_header (l_req, 'Authorization', l_negotiate_message); - - l_resp := utl_http.get_response(l_req); - - debug_response (l_resp); - - if l_resp.status_code = utl_http.HTTP_UNAUTHORIZED then - - -- received server challenge - utl_http.get_header_by_name(l_resp, 'WWW-Authenticate', l_value, 1); - - utl_http.end_response(l_resp); - - if substr(l_value, 1, 4) = 'NTLM' then - - -- get value - l_value := substr(l_value, 6); - - ntlm_util_pkg.parse_challenge_message (l_value, l_server_challenge, l_negotiate_flags); - - l_authenticate_message := 'NTLM ' || ntlm_util_pkg.get_authenticate_message(p_username, p_password, l_server_challenge, l_negotiate_flags); - debug_pkg.printf('Authenticate message: "%1"', l_authenticate_message); - - ------------ - -- Request 3 - ------------ - - -- sending NTLM message 3 - debug_pkg.printf(' '); - debug_pkg.printf(p_url); - l_req := utl_http.begin_request(p_url); - - utl_http.set_header (l_req, 'Connection', 'close'); - utl_http.set_header (l_req, 'Authorization', l_authenticate_message); - - l_resp := utl_http.get_response (l_req); - - debug_response (l_resp); - - -- this is already done inside debug_response - --l_returnvalue := get_response_body (l_resp); - - utl_http.end_response(l_resp); - - end if; - - end if; - - else - debug_pkg.printf('Server is not configured with NTLM security (missing "WWW-Authenticate: NTLM" header).'); - end if; - - end if; - - - utl_http.close_persistent_conns; - debug_pkg.printf('Persistent connection count (should be zero): %1', utl_http.get_persistent_conn_count); - - return l_returnvalue; - - end get_response_clob; @@ -697,4 +540,3 @@ end end_request; end ntlm_http_pkg; / - diff --git a/ora/ntlm_http_pkg.pks b/ora/ntlm_http_pkg.pks index 1cfe91f..34d1485 100755 --- a/ora/ntlm_http_pkg.pks +++ b/ora/ntlm_http_pkg.pks @@ -47,4 +47,3 @@ as end ntlm_http_pkg; / - From e72b80d4bc203900f441ff4f8a0ae0b5d71edcc3 Mon Sep 17 00:00:00 2001 From: Mark Sta Ana Date: Thu, 16 Apr 2015 16:01:33 +0100 Subject: [PATCH 06/21] Comment on TABLE_EXISTS_ACTION parameter --- ora/datapump_util_pkg.pkb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ora/datapump_util_pkg.pkb b/ora/datapump_util_pkg.pkb index 7726232..56d5c78 100755 --- a/ora/datapump_util_pkg.pkb +++ b/ora/datapump_util_pkg.pkb @@ -122,6 +122,8 @@ begin if p_table_data_only then dbms_datapump.metadata_filter (l_job_handle, 'INCLUDE_PATH_LIST', '''TABLE'''); -- note the double quotes -- TODO: investigate the "TABLE_EXISTS_ACTION" parameter + -- not sure what the context was for the TODO, but we use it to clear down tables before importing data. + -- Usage is dbms_datapump.set_parameter(l_job_handle, 'TABLE_EXISTS_ACTION', 'TRUNCATE'); end if; dbms_datapump.start_job (l_job_handle); From f11258a39ef07871879285b7c91c22777a1da11a Mon Sep 17 00:00:00 2001 From: JesperLorentzen Date: Fri, 17 Apr 2015 15:34:11 +0200 Subject: [PATCH 07/21] Performance was real bad for convert clob to CSV Replaced the p_csv_clob with a local and doing the append once of l_line_separator instead of multiple times --- ora/csv_util_pkg.pkb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ora/csv_util_pkg.pkb b/ora/csv_util_pkg.pkb index 9965a47..dafe216 100755 --- a/ora/csv_util_pkg.pkb +++ b/ora/csv_util_pkg.pkb @@ -221,6 +221,7 @@ function clob_to_csv (p_csv_clob in clob, p_separator in varchar2 := g_default_separator, p_skip_rows in number := 0) return t_csv_tab pipelined as + l_csv_clob clob; l_line_separator varchar2(2) := chr(13) || chr(10); l_last pls_integer; l_current pls_integer; @@ -243,7 +244,7 @@ begin Who Date Description ------ ---------- -------------------------------- MBR 31.03.2010 Created - + JLL 20.04.2015 Modified made an internal clob because || l_line_separator is very bad for performance */ -- If the file has a DOS newline (cr+lf), use that @@ -253,17 +254,18 @@ begin end if; l_last := 1; + l_csv_clob := p_csv_clob || l_line_separator; loop - l_current := dbms_lob.instr (p_csv_clob || l_line_separator, l_line_separator, l_last, 1); + l_current := dbms_lob.instr (l_csv_clob , l_line_separator, l_last, 1); exit when (nvl(l_current,0) = 0); l_line_number := l_line_number + 1; if l_from_line <= l_line_number then - l_line := dbms_lob.substr(p_csv_clob || l_line_separator, l_current - l_last + 1, l_last); + l_line := dbms_lob.substr(l_csv_clob, l_current - l_last + 1, l_last); --l_line := replace(l_line, l_line_separator, ''); l_line := replace(l_line, chr(10), ''); l_line := replace(l_line, chr(13), ''); From 2f3ef811c9964e54eb2ffbe895ac4fc10ef115d6 Mon Sep 17 00:00:00 2001 From: Brett Roper Date: Sun, 10 May 2015 12:43:55 +0800 Subject: [PATCH 08/21] remove invalid low end ascii. see: http://support.microsoft.com/kb/315580 --- ora/xlsx_builder_pkg.pkb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ora/xlsx_builder_pkg.pkb b/ora/xlsx_builder_pkg.pkb index 4f14713..064c19b 100755 --- a/ora/xlsx_builder_pkg.pkb +++ b/ora/xlsx_builder_pkg.pkb @@ -548,6 +548,19 @@ as end if; return 's="' || t_XfId || '"'; end; +-- + function clean_string (p_string in varchar2) + return varchar2 + is + invalid_ascii constant varchar2(32) := + chr(00)||chr(01)||chr(02)||chr(03)||chr(04)||chr(05)||chr(06)||chr(07)|| + chr(08)|| chr(11)||chr(12)|| chr(14)||chr(15)|| + chr(16)||chr(17)||chr(18)||chr(19)||chr(20)||chr(21)||chr(22)||chr(23)|| + chr(24)||chr(25)||chr(26)||chr(27)||chr(28)||chr(29)||chr(30)||chr(31) + ; + begin + return translate(translate( p_string, invalid_ascii, chr(1)), chr(0)||chr(1), ' '); + end; -- procedure cell ( p_col pls_integer @@ -584,7 +597,7 @@ as t_alignment tp_alignment := p_alignment; begin workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := workbook.strings.count(); - workbook.strings( workbook.strings.count() ) := p_value; + workbook.strings( workbook.strings.count() ) := clean_string( p_value ); if t_alignment.wrapText is null and instr( p_value, chr(13) ) > 0 then t_alignment.wrapText := true; @@ -633,7 +646,7 @@ as t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() ); begin workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := workbook.strings.count(); - workbook.strings( workbook.strings.count() ) := nvl( p_value, p_url ); + workbook.strings( workbook.strings.count() ) := clean_string( nvl( p_value, p_url ) ); workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := 't="s" ' || get_XfId( t_sheet, p_col, p_row, '', get_font( 'Calibri', p_theme => 10, p_underline => true ) ); t_ind := workbook.sheets( t_sheet ).hyperlinks.count() + 1; workbook.sheets( t_sheet ).hyperlinks( t_ind ).cell := alfan_col( p_col ) || p_row; From 674fe67e242ab45bcc65799af363ea1611c47f20 Mon Sep 17 00:00:00 2001 From: Morten Braten Date: Wed, 13 May 2015 19:27:40 +0200 Subject: [PATCH 09/21] Created coding standards document --- doc/coding_standards.txt | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 doc/coding_standards.txt diff --git a/doc/coding_standards.txt b/doc/coding_standards.txt new file mode 100644 index 0000000..7fbc6f7 --- /dev/null +++ b/doc/coding_standards.txt @@ -0,0 +1,41 @@ +Coding Standards +for the +Alexandria PL/SQL Utility Library +================================= + +Please follow the following coding conventions if you want to contribute to the library. +The best way to write compliant code is to familiarize yourself with the existing code and try to adapt a similar style. + +General +------- + +* Files should use Windows line endings. Any decent editor will understand both Windows and Unix line endings, but by sticking to Windows line endings the files can be viewed without problems even in "dumb" editors such as Notepad. +* Make sure your Git client is set to "checkout as-is, commit as-is" with respect to line endings. See https://help.github.com/articles/dealing-with-line-endings/ and http://stackoverflow.com/questions/10418975/how-to-change-line-ending-settings for more information. + +Case +---- +* All identifiers, built-in functions, packages and other code should be written in lowercase. +* Use underscores between words. +* CORRECT: get_invoice_totals +* WRONG: GET_INVOICE_TOTALS +* WRONG: getInvoiceTotals + +Indentation +----------- +* Identation size is 2 spaces +* Do not use tabs, always use spaces + +Naming Conventions +------------------ +* All packages should be postfixed with _pkg +* All parameters should be prefixed with p_ +* All local variables should be prefixed with l_ +* All private "module"-level (ie package body-level) variables should be prefixed with m_ +* All public "global" (ie package specification-level) variables should be prefixed with g_ +* All public "global" (ie package specification-level) constants should be prefixed with c_ (note: this is not done consistently in existing code, where constants are commonly prefixed with g_ instead, but new code should use the c_ prefix) + +Other +----- +* All program units (functions and procedures) must have a standard comment block that explains the purpose of the program unit, as well as a change log that includes the author's initials (max 3 characters), date (using format DD.MM.YYYY) and a description of the change +* All functions must define a local variable called "l_returnvalue" and the last statement in any function should be "return l_returnvalue;". Do not write functions with multiple return statements, and do not write procedures with any return statements. +* Always include (repeat) the name of the program unit in the final "end" statement at the end of the program unit. From d3af944d273b5a03fee92e04de2618807d8077eb Mon Sep 17 00:00:00 2001 From: Matthew Hasbach Date: Tue, 12 May 2015 17:50:06 -0400 Subject: [PATCH 10/21] string_util_pkg.str_to_bool_str now leverages string_util_pkg.str_to_bool in order to reduce code redundancy --- ora/string_util_pkg.pkb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ora/string_util_pkg.pkb b/ora/string_util_pkg.pkb index de009e6..028e4d8 100755 --- a/ora/string_util_pkg.pkb +++ b/ora/string_util_pkg.pkb @@ -694,10 +694,11 @@ begin Who Date Description ------ ---------- ------------------------------------- MBR 06.01.2009 Created + MJH 12.05.2015 Leverage string_util_pkg.str_to_bool in order to reduce code redundancy */ - if lower(p_str) in ('y', 'yes', 'true', '1') then + if str_to_bool(p_str) then l_returnvalue := g_yes; end if; From 01f129e2a58d051e3b1389ac7c0ce3a08345f240 Mon Sep 17 00:00:00 2001 From: Matthew Hasbach Date: Tue, 12 May 2015 17:37:29 -0400 Subject: [PATCH 11/21] Added string_util_pkg.is_str_alphanumeric --- ora/string_util_pkg.pkb | 22 ++++++++++++++++++++++ ora/string_util_pkg.pks | 3 +++ 2 files changed, 25 insertions(+) diff --git a/ora/string_util_pkg.pkb b/ora/string_util_pkg.pkb index de009e6..8c70058 100755 --- a/ora/string_util_pkg.pkb +++ b/ora/string_util_pkg.pkb @@ -487,6 +487,28 @@ begin end remove_non_alpha_chars; +function is_str_alphanumeric (p_str in varchar2) return boolean +as + l_returnvalue boolean; +begin + + /* + + Purpose: returns true if string is alphanumeric + + Who Date Description + ------ ---------- ------------------------------------- + MJH 12.05.2015 Created + + */ + + l_returnvalue := regexp_instr(p_str, '[^a-z|A-Z|0-9]') = 0; + + return l_returnvalue; + +end is_str_alphanumeric; + + function is_str_empty (p_str in varchar2) return boolean as l_returnvalue boolean; diff --git a/ora/string_util_pkg.pks b/ora/string_util_pkg.pks index 3fbc4db..a665e5d 100755 --- a/ora/string_util_pkg.pks +++ b/ora/string_util_pkg.pks @@ -92,6 +92,9 @@ as -- remove all non-alpha characters (A-Z) from string function remove_non_alpha_chars (p_str in varchar2) return varchar2; + -- returns true if string is alphanumeric + function is_str_alphanumeric (p_str in varchar2) return boolean; + -- returns true if string is "empty" (contains only whitespace characters) function is_str_empty (p_str in varchar2) return boolean; From 8e4a27db73372fde4dd1365da5dbd56e253750e7 Mon Sep 17 00:00:00 2001 From: Matthew Hasbach Date: Wed, 13 May 2015 18:00:45 -0400 Subject: [PATCH 12/21] Added string_util_pkg.is_str_alpha --- ora/string_util_pkg.pkb | 22 ++++++++++++++++++++++ ora/string_util_pkg.pks | 3 +++ 2 files changed, 25 insertions(+) diff --git a/ora/string_util_pkg.pkb b/ora/string_util_pkg.pkb index de009e6..9ba05ca 100755 --- a/ora/string_util_pkg.pkb +++ b/ora/string_util_pkg.pkb @@ -487,6 +487,28 @@ begin end remove_non_alpha_chars; +function is_str_alpha (p_str in varchar2) return boolean +as + l_returnvalue boolean; +begin + + /* + + Purpose: returns true if string only contains alpha characters + + Who Date Description + ------ ---------- ------------------------------------- + MJH 12.05.2015 Created + + */ + + l_returnvalue := regexp_instr(p_str, '[^a-z|A-Z]') = 0; + + return l_returnvalue; + +end is_str_alpha; + + function is_str_empty (p_str in varchar2) return boolean as l_returnvalue boolean; diff --git a/ora/string_util_pkg.pks b/ora/string_util_pkg.pks index 3fbc4db..62c0805 100755 --- a/ora/string_util_pkg.pks +++ b/ora/string_util_pkg.pks @@ -92,6 +92,9 @@ as -- remove all non-alpha characters (A-Z) from string function remove_non_alpha_chars (p_str in varchar2) return varchar2; + -- returns true if string only contains alpha characters + function is_str_alpha (p_str in varchar2) return boolean; + -- returns true if string is "empty" (contains only whitespace characters) function is_str_empty (p_str in varchar2) return boolean; From 8fdac96e663ee33dd638b901eaf10dc4f8db2a48 Mon Sep 17 00:00:00 2001 From: Matthew Hasbach Date: Tue, 12 May 2015 18:48:34 -0400 Subject: [PATCH 13/21] Added string_util_pkg.is_str_integer --- ora/string_util_pkg.pkb | 22 ++++++++++++++++++++++ ora/string_util_pkg.pks | 3 +++ 2 files changed, 25 insertions(+) diff --git a/ora/string_util_pkg.pkb b/ora/string_util_pkg.pkb index de009e6..2be224a 100755 --- a/ora/string_util_pkg.pkb +++ b/ora/string_util_pkg.pkb @@ -557,6 +557,28 @@ begin end is_str_number; +function is_str_integer (p_str in varchar2) return boolean +as + l_returnvalue boolean; +begin + + /* + + Purpose: returns true if string is an integer + + Who Date Description + ------ ---------- ------------------------------------- + MJH 12.05.2015 Created + + */ + + l_returnvalue := regexp_instr(p_str, '[^0-9]') = 0; + + return l_returnvalue; + +end is_str_integer; + + function short_str (p_str in varchar2, p_length in number, p_truncation_indicator in varchar2 := '...') return varchar2 diff --git a/ora/string_util_pkg.pks b/ora/string_util_pkg.pks index 3fbc4db..32561a9 100755 --- a/ora/string_util_pkg.pks +++ b/ora/string_util_pkg.pks @@ -100,6 +100,9 @@ as p_decimal_separator in varchar2 := null, p_thousand_separator in varchar2 := null) return boolean; + -- returns true if string is an integer + function is_str_integer (p_str in varchar2) return boolean; + -- returns substring and indicates if string has been truncated function short_str (p_str in varchar2, p_length in number, From 3643767bbc1dd02b0f823d379cc734fed5691104 Mon Sep 17 00:00:00 2001 From: Eric Olson Date: Sun, 24 May 2015 10:30:15 -0500 Subject: [PATCH 14/21] Get all nodes under Change XPath query to get all nodes under the node of interest, regardless of their depth. This concatenates the resulting text() nodes together. --- ora/ooxml_util_pkg.pkb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ora/ooxml_util_pkg.pkb b/ora/ooxml_util_pkg.pkb index 6cf17f5..97e7365 100755 --- a/ora/ooxml_util_pkg.pkb +++ b/ora/ooxml_util_pkg.pkb @@ -401,7 +401,7 @@ begin if l_type = 's' then l_string_index := to_number (l_returnvalue); l_xml := get_xml (p_xlsx, 'xl/sharedStrings.xml'); - l_returnvalue := xml_util_pkg.extract_value (l_xml, '/sst/si[' || (l_string_index + 1) || ']/t/text()', g_namespace_xlsx_sharedstrings); + l_returnvalue := xml_util_pkg.extract_value (l_xml, '/sst/si[' || (l_string_index + 1) || ']//t/text()', g_namespace_xlsx_sharedstrings); end if; return l_returnvalue; @@ -453,7 +453,7 @@ begin if l_type = 's' then l_string_index := to_number (l_returnvalue(i)); - l_returnvalue(i) := xml_util_pkg.extract_value (l_shared_strings, '/sst/si[' || (l_string_index + 1) || ']/t/text()', g_namespace_xlsx_sharedstrings); + l_returnvalue(i) := xml_util_pkg.extract_value (l_shared_strings, '/sst/si[' || (l_string_index + 1) || ']//t/text()', g_namespace_xlsx_sharedstrings); end if; end loop; From 6e4cd51dc65f1db458a8c62cb49898432c1738af Mon Sep 17 00:00:00 2001 From: mortenbra Date: Fri, 12 Jun 2015 11:07:58 +0200 Subject: [PATCH 15/21] PL/JSON moved from SourgeForge to GitHub --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d855fc..42dadd3 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ This library is a collection of various utility packages for PL/SQL, as well as ##Parse JSON using PL/SQL - * http://sourceforge.net/projects/pljson/ + * https://github.com/pljson/pljson * http://reseau.erasme.org/pl-sql-library-for-JSON?lang=en From e1bc770c3187e3f197f8c60dfbcdc454703d6ddd Mon Sep 17 00:00:00 2001 From: Morten Braten Date: Mon, 20 Jul 2015 21:53:00 +0200 Subject: [PATCH 16/21] Install scripts now reference folders with forward slash --- setup/install.sql | 188 ++++++++++++++++++------------------ setup/install_amazon.sql | 8 +- setup/install_core.sql | 52 +++++----- setup/install_google.sql | 12 +-- setup/install_inet.sql | 36 +++---- setup/install_microsoft.sql | 24 ++--- setup/install_owa_apex.sql | 8 +- setup/install_xml.sql | 12 +-- 8 files changed, 170 insertions(+), 170 deletions(-) diff --git a/setup/install.sql b/setup/install.sql index 76d86b3..5d79921 100755 --- a/setup/install.sql +++ b/setup/install.sql @@ -7,103 +7,103 @@ prompt Creating types prompt Creating package specifications -@..\ora\amazon_aws_auth_pkg.pks -@..\ora\amazon_aws_s3_pkg.pks -@..\ora\apex_util_pkg.pks -@..\ora\crypto_util_pkg.pks -@..\ora\csv_util_pkg.pks -@..\ora\datapump_util_pkg.pks -@..\ora\date_util_pkg.pks -@..\ora\debug_pkg.pks -@..\ora\encode_util_pkg.pks -@..\ora\file_util_pkg.pks -@..\ora\flex_ws_api.pks -@..\ora\ftp_util_pkg.pks -@..\ora\gis_util_pkg.pks -@..\ora\google_maps_pkg.pks -@..\ora\google_maps_js_pkg.pks -@..\ora\google_translate_pkg.pks -@..\ora\html_util_pkg.pks -@..\ora\http_util_pkg.pks -@..\ora\icalendar_util_pkg.pks -@..\ora\image_util_pkg.pks -@..\ora\json_util_pkg.pks -@..\ora\math_util_pkg.pks -@..\ora\ms_ews_util_pkg.pks -@..\ora\ntlm_util_pkg.pks -@..\ora\ntlm_http_pkg.pks -@..\ora\ooxml_util_pkg.pks -@..\ora\owa_util_pkg.pks -@..\ora\pdf_builder_pkg.pks -@..\ora\random_util_pkg.pks -@..\ora\raw_util_pkg.pks -@..\ora\regexp_util_pkg.pks -@..\ora\rss_util_pkg.pks -@..\ora\soap_server_pkg.pks -@..\ora\sql_builder_pkg.pks -@..\ora\sql_util_pkg.pks -@..\ora\string_util_pkg.pks -@..\ora\sylk_util_pkg.pks -@..\ora\t_soap_envelope.pks -@..\ora\uri_template_util_pkg.pks -@..\ora\validation_util_pkg.pks -@..\ora\web_util_pkg.pks -@..\ora\xlsx_builder_pkg.pks -@..\ora\xml_builder_pkg.pks -@..\ora\xml_dataset_pkg.pks -@..\ora\xml_stylesheet_pkg.pks -@..\ora\xml_util_pkg.pks -@..\ora\zip_util_pkg.pks +@../ora/amazon_aws_auth_pkg.pks +@../ora/amazon_aws_s3_pkg.pks +@../ora/apex_util_pkg.pks +@../ora/crypto_util_pkg.pks +@../ora/csv_util_pkg.pks +@../ora/datapump_util_pkg.pks +@../ora/date_util_pkg.pks +@../ora/debug_pkg.pks +@../ora/encode_util_pkg.pks +@../ora/file_util_pkg.pks +@../ora/flex_ws_api.pks +@../ora/ftp_util_pkg.pks +@../ora/gis_util_pkg.pks +@../ora/google_maps_pkg.pks +@../ora/google_maps_js_pkg.pks +@../ora/google_translate_pkg.pks +@../ora/html_util_pkg.pks +@../ora/http_util_pkg.pks +@../ora/icalendar_util_pkg.pks +@../ora/image_util_pkg.pks +@../ora/json_util_pkg.pks +@../ora/math_util_pkg.pks +@../ora/ms_ews_util_pkg.pks +@../ora/ntlm_util_pkg.pks +@../ora/ntlm_http_pkg.pks +@../ora/ooxml_util_pkg.pks +@../ora/owa_util_pkg.pks +@../ora/pdf_builder_pkg.pks +@../ora/random_util_pkg.pks +@../ora/raw_util_pkg.pks +@../ora/regexp_util_pkg.pks +@../ora/rss_util_pkg.pks +@../ora/soap_server_pkg.pks +@../ora/sql_builder_pkg.pks +@../ora/sql_util_pkg.pks +@../ora/string_util_pkg.pks +@../ora/sylk_util_pkg.pks +@../ora/t_soap_envelope.pks +@../ora/uri_template_util_pkg.pks +@../ora/validation_util_pkg.pks +@../ora/web_util_pkg.pks +@../ora/xlsx_builder_pkg.pks +@../ora/xml_builder_pkg.pks +@../ora/xml_dataset_pkg.pks +@../ora/xml_stylesheet_pkg.pks +@../ora/xml_util_pkg.pks +@../ora/zip_util_pkg.pks prompt Creating package bodies -@..\ora\amazon_aws_auth_pkg.pkb -@..\ora\amazon_aws_s3_pkg.pkb -@..\ora\apex_util_pkg.pkb -@..\ora\crypto_util_pkg.pkb -@..\ora\csv_util_pkg.pkb -@..\ora\datapump_util_pkg.pkb -@..\ora\date_util_pkg.pkb -@..\ora\debug_pkg.pkb -@..\ora\encode_util_pkg.pkb -@..\ora\file_util_pkg.pkb -@..\ora\flex_ws_api.pkb -@..\ora\ftp_util_pkg.pkb -@..\ora\gis_util_pkg.pkb -@..\ora\google_maps_pkg.pkb -@..\ora\google_maps_js_pkg.pkb -@..\ora\google_translate_pkg.pkb -@..\ora\html_util_pkg.pkb -@..\ora\http_util_pkg.pkb -@..\ora\icalendar_util_pkg.pkb -@..\ora\image_util_pkg.pkb -@..\ora\json_util_pkg.pkb -@..\ora\math_util_pkg.pkb -@..\ora\ms_ews_util_pkg.pkb -@..\ora\ntlm_util_pkg.pkb -@..\ora\ntlm_http_pkg.pkb -@..\ora\ooxml_util_pkg.pkb -@..\ora\owa_util_pkg.pkb -@..\ora\pdf_builder_pkg.pkb -@..\ora\random_util_pkg.pkb -@..\ora\raw_util_pkg.pkb -@..\ora\regexp_util_pkg.pkb -@..\ora\rss_util_pkg.pkb -@..\ora\soap_server_pkg.pkb -@..\ora\sql_builder_pkg.pkb -@..\ora\sql_util_pkg.pkb -@..\ora\string_util_pkg.pkb -@..\ora\sylk_util_pkg.pkb -@..\ora\t_soap_envelope.pkb -@..\ora\uri_template_util_pkg.pkb -@..\ora\validation_util_pkg.pkb -@..\ora\web_util_pkg.pkb -@..\ora\xlsx_builder_pkg.pkb -@..\ora\xml_builder_pkg.pkb -@..\ora\xml_dataset_pkg.pkb -@..\ora\xml_stylesheet_pkg.pkb -@..\ora\xml_util_pkg.pkb -@..\ora\zip_util_pkg.pkb +@../ora/amazon_aws_auth_pkg.pkb +@../ora/amazon_aws_s3_pkg.pkb +@../ora/apex_util_pkg.pkb +@../ora/crypto_util_pkg.pkb +@../ora/csv_util_pkg.pkb +@../ora/datapump_util_pkg.pkb +@../ora/date_util_pkg.pkb +@../ora/debug_pkg.pkb +@../ora/encode_util_pkg.pkb +@../ora/file_util_pkg.pkb +@../ora/flex_ws_api.pkb +@../ora/ftp_util_pkg.pkb +@../ora/gis_util_pkg.pkb +@../ora/google_maps_pkg.pkb +@../ora/google_maps_js_pkg.pkb +@../ora/google_translate_pkg.pkb +@../ora/html_util_pkg.pkb +@../ora/http_util_pkg.pkb +@../ora/icalendar_util_pkg.pkb +@../ora/image_util_pkg.pkb +@../ora/json_util_pkg.pkb +@../ora/math_util_pkg.pkb +@../ora/ms_ews_util_pkg.pkb +@../ora/ntlm_util_pkg.pkb +@../ora/ntlm_http_pkg.pkb +@../ora/ooxml_util_pkg.pkb +@../ora/owa_util_pkg.pkb +@../ora/pdf_builder_pkg.pkb +@../ora/random_util_pkg.pkb +@../ora/raw_util_pkg.pkb +@../ora/regexp_util_pkg.pkb +@../ora/rss_util_pkg.pkb +@../ora/soap_server_pkg.pkb +@../ora/sql_builder_pkg.pkb +@../ora/sql_util_pkg.pkb +@../ora/string_util_pkg.pkb +@../ora/sylk_util_pkg.pkb +@../ora/t_soap_envelope.pkb +@../ora/uri_template_util_pkg.pkb +@../ora/validation_util_pkg.pkb +@../ora/web_util_pkg.pkb +@../ora/xlsx_builder_pkg.pkb +@../ora/xml_builder_pkg.pkb +@../ora/xml_dataset_pkg.pkb +@../ora/xml_stylesheet_pkg.pkb +@../ora/xml_util_pkg.pkb +@../ora/zip_util_pkg.pkb prompt Done! diff --git a/setup/install_amazon.sql b/setup/install_amazon.sql index 7761dd9..f23b148 100755 --- a/setup/install_amazon.sql +++ b/setup/install_amazon.sql @@ -4,13 +4,13 @@ set scan off; prompt Creating AMAZON package specifications -@..\ora\amazon_aws_auth_pkg.pks -@..\ora\amazon_aws_s3_pkg.pks +@../ora/amazon_aws_auth_pkg.pks +@../ora/amazon_aws_s3_pkg.pks prompt Creating AMAZON package bodies -@..\ora\amazon_aws_auth_pkg.pkb -@..\ora\amazon_aws_s3_pkg.pkb +@../ora/amazon_aws_auth_pkg.pkb +@../ora/amazon_aws_s3_pkg.pkb prompt Done! diff --git a/setup/install_core.sql b/setup/install_core.sql index 74e35e0..740c3f5 100755 --- a/setup/install_core.sql +++ b/setup/install_core.sql @@ -7,35 +7,35 @@ prompt Creating CORE types prompt Creating CORE package specifications -@..\ora\crypto_util_pkg.pks -@..\ora\date_util_pkg.pks -@..\ora\debug_pkg.pks -@..\ora\encode_util_pkg.pks -@..\ora\file_util_pkg.pks -@..\ora\math_util_pkg.pks -@..\ora\random_util_pkg.pks -@..\ora\raw_util_pkg.pks -@..\ora\regexp_util_pkg.pks -@..\ora\sql_util_pkg.pks -@..\ora\string_util_pkg.pks -@..\ora\xml_util_pkg.pks -@..\ora\zip_util_pkg.pks +@../ora/crypto_util_pkg.pks +@../ora/date_util_pkg.pks +@../ora/debug_pkg.pks +@../ora/encode_util_pkg.pks +@../ora/file_util_pkg.pks +@../ora/math_util_pkg.pks +@../ora/random_util_pkg.pks +@../ora/raw_util_pkg.pks +@../ora/regexp_util_pkg.pks +@../ora/sql_util_pkg.pks +@../ora/string_util_pkg.pks +@../ora/xml_util_pkg.pks +@../ora/zip_util_pkg.pks prompt Creating CORE package bodies -@..\ora\crypto_util_pkg.pkb -@..\ora\date_util_pkg.pkb -@..\ora\debug_pkg.pkb -@..\ora\encode_util_pkg.pkb -@..\ora\file_util_pkg.pkb -@..\ora\math_util_pkg.pkb -@..\ora\random_util_pkg.pkb -@..\ora\raw_util_pkg.pkb -@..\ora\regexp_util_pkg.pkb -@..\ora\sql_util_pkg.pkb -@..\ora\string_util_pkg.pkb -@..\ora\xml_util_pkg.pkb -@..\ora\zip_util_pkg.pkb +@../ora/crypto_util_pkg.pkb +@../ora/date_util_pkg.pkb +@../ora/debug_pkg.pkb +@../ora/encode_util_pkg.pkb +@../ora/file_util_pkg.pkb +@../ora/math_util_pkg.pkb +@../ora/random_util_pkg.pkb +@../ora/raw_util_pkg.pkb +@../ora/regexp_util_pkg.pkb +@../ora/sql_util_pkg.pkb +@../ora/string_util_pkg.pkb +@../ora/xml_util_pkg.pkb +@../ora/zip_util_pkg.pkb prompt Done! diff --git a/setup/install_google.sql b/setup/install_google.sql index 521ed42..431b87f 100755 --- a/setup/install_google.sql +++ b/setup/install_google.sql @@ -4,15 +4,15 @@ set scan off; prompt Creating GOOGLE package specifications -@..\ora\google_maps_pkg.pks -@..\ora\google_maps_js_pkg.pks -@..\ora\google_translate_pkg.pks +@../ora/google_maps_pkg.pks +@../ora/google_maps_js_pkg.pks +@../ora/google_translate_pkg.pks prompt Creating GOOGLE package bodies -@..\ora\google_maps_pkg.pkb -@..\ora\google_maps_js_pkg.pkb -@..\ora\google_translate_pkg.pkb +@../ora/google_maps_pkg.pkb +@../ora/google_maps_js_pkg.pkb +@../ora/google_translate_pkg.pkb prompt Done! diff --git a/setup/install_inet.sql b/setup/install_inet.sql index 80b3d38..a5eb305 100755 --- a/setup/install_inet.sql +++ b/setup/install_inet.sql @@ -3,27 +3,27 @@ set scan off; prompt Creating INET package specifications -@..\ora\flex_ws_api.pks -@..\ora\ftp_util_pkg.pks -@..\ora\html_util_pkg.pks -@..\ora\http_util_pkg.pks -@..\ora\icalendar_util_pkg.pks -@..\ora\json_util_pkg.pks -@..\ora\rss_util_pkg.pks -@..\ora\t_soap_envelope.pks -@..\ora\web_util_pkg.pks +@../ora/flex_ws_api.pks +@../ora/ftp_util_pkg.pks +@../ora/html_util_pkg.pks +@../ora/http_util_pkg.pks +@../ora/icalendar_util_pkg.pks +@../ora/json_util_pkg.pks +@../ora/rss_util_pkg.pks +@../ora/t_soap_envelope.pks +@../ora/web_util_pkg.pks prompt Creating INET package bodies -@..\ora\flex_ws_api.pkb -@..\ora\ftp_util_pkg.pkb -@..\ora\html_util_pkg.pkb -@..\ora\http_util_pkg.pkb -@..\ora\icalendar_util_pkg.pkb -@..\ora\json_util_pkg.pkb -@..\ora\rss_util_pkg.pkb -@..\ora\t_soap_envelope.pkb -@..\ora\web_util_pkg.pkb +@../ora/flex_ws_api.pkb +@../ora/ftp_util_pkg.pkb +@../ora/html_util_pkg.pkb +@../ora/http_util_pkg.pkb +@../ora/icalendar_util_pkg.pkb +@../ora/json_util_pkg.pkb +@../ora/rss_util_pkg.pkb +@../ora/t_soap_envelope.pkb +@../ora/web_util_pkg.pkb prompt Done! diff --git a/setup/install_microsoft.sql b/setup/install_microsoft.sql index 51c7ca8..41e2d2d 100755 --- a/setup/install_microsoft.sql +++ b/setup/install_microsoft.sql @@ -3,21 +3,21 @@ set scan off; prompt Creating MICROSOFT package specifications -@..\ora\ms_ews_util_pkg.pks -@..\ora\ntlm_util_pkg.pks -@..\ora\ntlm_http_pkg.pks -@..\ora\ooxml_util_pkg.pks -@..\ora\xlsx_builder_pkg.pks -@..\ora\xml_stylesheet_pkg.pks +@../ora/ms_ews_util_pkg.pks +@../ora/ntlm_util_pkg.pks +@../ora/ntlm_http_pkg.pks +@../ora/ooxml_util_pkg.pks +@../ora/xlsx_builder_pkg.pks +@../ora/xml_stylesheet_pkg.pks prompt Creating MICROSOFT package bodies -@..\ora\ms_ews_util_pkg.pkb -@..\ora\ntlm_util_pkg.pkb -@..\ora\ntlm_http_pkg.pkb -@..\ora\ooxml_util_pkg.pkb -@..\ora\xlsx_builder_pkg.pkb -@..\ora\xml_stylesheet_pkg.pkb +@../ora/ms_ews_util_pkg.pkb +@../ora/ntlm_util_pkg.pkb +@../ora/ntlm_http_pkg.pkb +@../ora/ooxml_util_pkg.pkb +@../ora/xlsx_builder_pkg.pkb +@../ora/xml_stylesheet_pkg.pkb prompt Done! diff --git a/setup/install_owa_apex.sql b/setup/install_owa_apex.sql index d825d3a..74036c2 100755 --- a/setup/install_owa_apex.sql +++ b/setup/install_owa_apex.sql @@ -4,13 +4,13 @@ set scan off; prompt Creating ORACLE APEX and OWA utility package specifications -@..\ora\apex_util_pkg.pks -@..\ora\owa_util_pkg.pks +@../ora/apex_util_pkg.pks +@../ora/owa_util_pkg.pks prompt Creating ORACLE APEX and OWA utility package bodies -@..\ora\apex_util_pkg.pkb -@..\ora\owa_util_pkg.pkb +@../ora/apex_util_pkg.pkb +@../ora/owa_util_pkg.pkb prompt Done! diff --git a/setup/install_xml.sql b/setup/install_xml.sql index 48b3802..602b7ed 100755 --- a/setup/install_xml.sql +++ b/setup/install_xml.sql @@ -4,15 +4,15 @@ set scan off; prompt Creating XML package specifications -@..\ora\xml_builder_pkg.pks -@..\ora\xml_dataset_pkg.pks -@..\ora\xml_stylesheet_pkg.pks +@../ora/xml_builder_pkg.pks +@../ora/xml_dataset_pkg.pks +@../ora/xml_stylesheet_pkg.pks prompt Creating XML package bodies -@..\ora\xml_builder_pkg.pkb -@..\ora\xml_dataset_pkg.pkb -@..\ora\xml_stylesheet_pkg.pkb +@../ora/xml_builder_pkg.pkb +@../ora/xml_dataset_pkg.pkb +@../ora/xml_stylesheet_pkg.pkb prompt Done! From d2e71075effb843bbb2b7388d0eec22f242e3d7b Mon Sep 17 00:00:00 2001 From: Morten Braten Date: Thu, 24 Sep 2015 19:06:03 +0200 Subject: [PATCH 17/21] Fix for empty parameter name --- ora/string_util_pkg.pkb | 46 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/ora/string_util_pkg.pkb b/ora/string_util_pkg.pkb index 8999916..b1b168b 100755 --- a/ora/string_util_pkg.pkb +++ b/ora/string_util_pkg.pkb @@ -345,7 +345,6 @@ as l_temp_str t_max_pl_varchar2; l_begin_pos pls_integer; l_end_pos pls_integer; - begin @@ -359,33 +358,38 @@ begin Who Date Description ------ ---------- ------------------------------------- MBR 16.05.2007 Created + MBR 24.09.2015 If parameter name not specified (null), then return null */ - -- get the starting position of the param name - l_begin_pos:=instr(p_param_string, p_param_name || p_value_separator); + if p_param_name is not null then - if l_begin_pos = 0 then - l_returnvalue:=null; - else + -- get the starting position of the param name + l_begin_pos:=instr(p_param_string, p_param_name || p_value_separator); - -- trim off characters before param value begins, including param name - l_temp_str:=substr(p_param_string, l_begin_pos, length(p_param_string) - l_begin_pos + 1); - l_temp_str:=del_str(l_temp_str, 1, length(p_param_name || p_value_separator)); - - -- now find the first next occurence of the character delimiting the params - -- if delimiter not found, return the rest of the string - - l_end_pos:=instr(l_temp_str, p_param_separator); - if l_end_pos = 0 then - l_end_pos:=length(l_temp_str); + if l_begin_pos = 0 then + l_returnvalue:=null; else - -- strip off delimiter - l_end_pos:=l_end_pos - 1; - end if; - -- retrieve the value - l_returnvalue:=copy_str(l_temp_str, 1, l_end_pos); + -- trim off characters before param value begins, including param name + l_temp_str:=substr(p_param_string, l_begin_pos, length(p_param_string) - l_begin_pos + 1); + l_temp_str:=del_str(l_temp_str, 1, length(p_param_name || p_value_separator)); + + -- now find the first next occurence of the character delimiting the params + -- if delimiter not found, return the rest of the string + + l_end_pos:=instr(l_temp_str, p_param_separator); + if l_end_pos = 0 then + l_end_pos:=length(l_temp_str); + else + -- strip off delimiter + l_end_pos:=l_end_pos - 1; + end if; + + -- retrieve the value + l_returnvalue:=copy_str(l_temp_str, 1, l_end_pos); + + end if; end if; From 52546801261581ddc2e373a8417886444ae802f0 Mon Sep 17 00:00:00 2001 From: Morten Braten Date: Thu, 24 Sep 2015 19:08:17 +0200 Subject: [PATCH 18/21] Added get_date_tab to generate table of dates --- demos/date_util_pkg_demo.sql | 6 +++++ doc/changelog.txt | 7 ++++++ ora/date_util_pkg.pkb | 46 ++++++++++++++++++++++++++++++++++++ ora/date_util_pkg.pks | 5 ++++ 4 files changed, 64 insertions(+) diff --git a/demos/date_util_pkg_demo.sql b/demos/date_util_pkg_demo.sql index 799b9e7..49a44fe 100755 --- a/demos/date_util_pkg_demo.sql +++ b/demos/date_util_pkg_demo.sql @@ -3,3 +3,9 @@ select * from table(date_util_pkg.explode_month(2011, 2)) +-- generate a table of dates based on a "calendar string" +-- for calendar string syntax, see http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sched.htm#i1009923 +-- a start and stop date can be specified, it not specified it defaults to a year from now + +select t.column_value +from table(date_util_pkg.get_date_tab('FREQ=WEEKLY; BYDAY=MON,WED,FRI', trunc(sysdate), trunc(sysdate+90))) t \ No newline at end of file diff --git a/doc/changelog.txt b/doc/changelog.txt index 83685d8..87da56e 100755 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,4 +1,11 @@ +Version 1.7.1 +============= + +- Added sms_util_pkg +- Minor enhancements to date_util_pkg +- Minor enhancements to string_util_pkg + Version 1.7.0 ============= - Split installation script into various modules (core, xml, amazon, microsoft, etc.) diff --git a/ora/date_util_pkg.pkb b/ora/date_util_pkg.pkb index 2ca8b69..b08eeb1 100755 --- a/ora/date_util_pkg.pkb +++ b/ora/date_util_pkg.pkb @@ -628,5 +628,51 @@ begin end explode_month; +function get_date_tab (p_calendar_string in varchar2, + p_from_date in date := null, + p_to_date in date := null) return t_date_array pipelined +as + l_from_date date := coalesce(p_from_date, sysdate); + l_to_date date := coalesce(p_to_date, add_months(l_from_date,12)); + l_date_after date; + l_next_date date; +begin + + /* + + Purpose: get table of dates based on specified calendar string + + Remarks: see https://oraclesponge.wordpress.com/2010/08/18/generating-lists-of-dates-in-oracle-the-dbms_scheduler-way/ + see http://www.kibeha.dk/2014/12/date-row-generator-with-dbmsscheduler.html + + Who Date Description + ------ ---------- -------------------------------- + MBR 24.09.2015 Created + + */ + + l_date_after := l_from_date - 1; + + loop + + dbms_scheduler.evaluate_calendar_string ( + calendar_string => p_calendar_string, + start_date => l_from_date, + return_date_after => l_date_after, + next_run_date => l_next_date + ); + + exit when l_next_date > l_to_date; + + pipe row (l_next_date); + + l_date_after := l_next_date; + + end loop; + + return; + +end get_date_tab; + end date_util_pkg; / diff --git a/ora/date_util_pkg.pks b/ora/date_util_pkg.pks index 6883f65..b9c2e89 100755 --- a/ora/date_util_pkg.pks +++ b/ora/date_util_pkg.pks @@ -103,5 +103,10 @@ as function explode_month (p_year in number, p_month in number) return t_period_date_tab pipelined; + -- get table of dates based on specified calendar string + function get_date_tab (p_calendar_string in varchar2, + p_from_date in date := null, + p_to_date in date := null) return t_date_array pipelined; + end date_util_pkg; / From 2213e3df2f24a00322239d442c54bf31039f2588 Mon Sep 17 00:00:00 2001 From: Morten Braten Date: Thu, 24 Sep 2015 19:09:35 +0200 Subject: [PATCH 19/21] Added SMS utility package --- demos/sms_util_pkg_demo.sql | 40 +++++++ ora/sms_util_pkg.pkb | 209 ++++++++++++++++++++++++++++++++++++ ora/sms_util_pkg.pks | 54 ++++++++++ setup/install.sql | 2 + setup/synonyms.sql | 1 + 5 files changed, 306 insertions(+) create mode 100644 demos/sms_util_pkg_demo.sql create mode 100644 ora/sms_util_pkg.pkb create mode 100644 ora/sms_util_pkg.pks diff --git a/demos/sms_util_pkg_demo.sql b/demos/sms_util_pkg_demo.sql new file mode 100644 index 0000000..690e9eb --- /dev/null +++ b/demos/sms_util_pkg_demo.sql @@ -0,0 +1,40 @@ + +-- send sms (text message) via gateway +-- the configuration must be adapted to a specific gateway api +-- for example, see http://www.smsglobal.com/http-api/ + +-- sign up with a gateway provider to obtain a username and password + +/* + +-- the following tags can be used in the url template + +#username# +#password# +#message# +#to# +#from# +#attr1# +#attr2# +#attr3# + +*/ + +declare + l_config sms_util_pkg.t_gateway_config; +begin + -- configure gateway + -- remember you may have to open this hostname in the database Network ACL + l_config.send_sms_url := 'http://www.smsglobal.com/http-api.php?action=sendsms&user=#username#r&password=#password#&from=#from#&to=#to#& +text=#message#'; + l_config.username := 'testuser'; + l_config.password := 'secret'; + l_config.response_format := sms_util_pkg.g_format_custom; + l_config.response_error_parser := 'my_package.my_error_parser'; -- this is a function that accepts a clob and returns a varchar2 + sms_util_pkg.set_gateway_config (l_config); + -- if using HTTPS you need to set up an Oracle wallet with the certificate + -- sms_util_pkg.set_wallet (p_wallet_path => '/path/to/wallet/', p_wallet_password => 'somesecret'); + -- send the message + sms_util_pkg.sends_sms (p_message => 'Hello SMS World', p_to => 123456789, p_from => 'BobSacamano'); +end; +/ \ No newline at end of file diff --git a/ora/sms_util_pkg.pkb b/ora/sms_util_pkg.pkb new file mode 100644 index 0000000..5976e6f --- /dev/null +++ b/ora/sms_util_pkg.pkb @@ -0,0 +1,209 @@ +create or replace package body sms_util_pkg +as + + /* + + Purpose: Package handles sending of SMS (Short Message Service) to mobile phones via an SMS gateway + + Remarks: The package provides a generic interface and attempts to support "any" SMS gateway that provides an HTTP(S) (GET) interface + + Who Date Description + ------ ---------- -------------------------------- + MBR 24.08.2014 Created + + */ + + g_gateway_config t_gateway_config; + + g_wallet_path string_util_pkg.t_max_db_varchar2; + g_wallet_password string_util_pkg.t_max_db_varchar2; + + +function make_request (p_url in varchar2, + p_body in clob := null, + p_http_method in varchar2 := 'POST', + p_username in varchar2 := null, + p_password in varchar2 := null) return clob +as + l_returnvalue clob; +begin + + /* + + Purpose: make HTTP request + + Remarks: + + Who Date Description + ------ ---------- -------------------------------- + MBR 23.08.2014 Created + + */ + + debug_pkg.printf('%1 %2', p_http_method, p_url); + + l_returnvalue := apex_web_service.make_rest_request( + p_url => p_url, + p_http_method => p_http_method, + p_body => p_body, + p_username => p_username, + p_password => p_password, + p_wallet_path => g_wallet_path, + p_wallet_pwd => g_wallet_password + ); + + return l_returnvalue; + +end make_request; + + +procedure check_response_for_errors (p_response in clob) +as + l_xml xmltype; + l_error_message string_util_pkg.t_max_pl_varchar2; +begin + + /* + + Purpose: check response for errors + + Remarks: error handling is different for every SMS gateway (API provider) + + Who Date Description + ------ ---------- -------------------------------- + MBR 24.08.2014 Created + + */ + + debug_pkg.printf('response length = %1', length(p_response)); + debug_pkg.printf('first 32K characters of response = %1', substr(p_response,1,32000)); + + if g_gateway_config.response_format = g_format_xml then + + begin + l_xml := xmltype (p_response); + debug_pkg.printf('response converted to valid XML'); + if l_xml.existsnode (g_gateway_config.response_error_path) = 1 then + debug_pkg.printf('error path node found, attempting to retrieve it'); + l_error_message := l_xml.extract(g_gateway_config.response_error_path, g_gateway_config.response_error_namespace).getstringval(); + else + debug_pkg.printf('error path node not found, assuming no errors'); + end if; + exception + when others then + l_error_message := sqlerrm; + end; + + elsif g_gateway_config.response_format = g_format_custom then + + -- parse errors from response based on custom PL/SQL parsing function specified by the user + -- this function should take a single clob parameter (the response from the SMS gateway) and return a varchar2 if an error is found in the response + execute immediate 'begin sms_util_pkg.g_exec_result_string := ' || dbms_assert.sql_object_name(g_gateway_config.response_error_parser) || ' (:b1); end;' using p_response; + l_error_message := g_exec_result_string; + + else + -- TODO: implement JSON parsing using APEX_JSON (if Apex 5+ is installed) + raise_application_error (-20000, 'Response format ' || g_gateway_config.response_format || ' not supported or not implemented!'); + end if; + + if l_error_message is not null then + raise_application_error (-20000, 'The SMS gateway returned an error: ' || l_error_message, true); + end if; + +end check_response_for_errors; + + +procedure set_wallet (p_wallet_path in varchar2, + p_wallet_password in varchar2) +as +begin + + /* + + Purpose: set SSL wallet properties + + Remarks: + + Who Date Description + ------ ---------- -------------------------------- + MBR 23.08.2014 Created + + */ + + g_wallet_path := p_wallet_path; + g_wallet_password := p_wallet_password; + +end set_wallet; + + +procedure set_gateway_config (p_gateway_config in t_gateway_config) +as +begin + + /* + + Purpose: set gateway configuration + + Remarks: + + Who Date Description + ------ ---------- -------------------------------- + MBR 23.08.2014 Created + + */ + + g_gateway_config := p_gateway_config; + +end set_gateway_config; + + +procedure send_sms (p_message in varchar2, + p_to in varchar2, + p_from in varchar2, + p_attr1 in varchar2 := null, + p_attr2 in varchar2 := null, + p_attr3 in varchar2 := null, + p_username in varchar2 := null, + p_password in varchar2 := null) +as + l_url string_util_pkg.t_max_pl_varchar2; + l_response clob; + + function url_escape (p_text in varchar2) return varchar2 + as + begin + return utl_url.escape (p_text, escape_reserved_chars => false, url_charset => 'UTF8'); + end url_escape; + +begin + + /* + + Purpose: send SMS message + + Remarks: + + Who Date Description + ------ ---------- -------------------------------- + MBR 24.08.2014 Created + + */ + + -- TODO: assert that configuration exists + + l_url := string_util_pkg.multi_replace ( + g_gateway_config.send_sms_url, + t_str_array('#username#', '#password#', '#message#', '#to#', '#from#', '#attr1#', '#attr2#', '#attr3#'), + t_str_array(coalesce(p_username, g_gateway_config.username), coalesce(p_password, g_gateway_config.password), url_escape(p_message), url_escape(p_to), url_escape(p_from), p_attr1, p_attr2, p_attr3) + ); + + l_response := make_request (p_url => l_url, p_http_method => 'GET'); + + check_response_for_errors (l_response); + +end send_sms; + + +end sms_util_pkg; +/ + diff --git a/ora/sms_util_pkg.pks b/ora/sms_util_pkg.pks new file mode 100644 index 0000000..5a0f8a3 --- /dev/null +++ b/ora/sms_util_pkg.pks @@ -0,0 +1,54 @@ +create or replace package sms_util_pkg +as + + /* + + Purpose: Package handles sending of SMS (Short Message Service) to mobile phones via an SMS gateway + + Remarks: The package provides a generic interface and attempts to support any SMS gateway that provides an HTTP(S) (GET) interface + + Who Date Description + ------ ---------- -------------------------------- + MBR 24.08.2014 Created + + */ + + -- gateway configuration + type t_gateway_config is record ( + send_sms_url varchar2(4000), + username varchar2(255), + password varchar2(255), + response_format varchar2(30), + response_error_path varchar2(4000), -- either an xpath or jsonpath expression + response_error_namespace varchar2(4000), -- xml namespace + response_error_parser varchar2(4000) -- a custom PL/SQL error parsing function (must accept a clob parameter and return a varchar2 containing error message) + ); + + -- response formats + g_format_xml constant varchar2(255) := 'xml'; + g_format_json constant varchar2(255) := 'json'; + g_format_custom constant varchar2(255) := 'custom'; + + -- internal variable used for dynamic PL/SQL evaluation + g_exec_result_string varchar2(4000); + + -- set SSL wallet properties + procedure set_wallet (p_wallet_path in varchar2, + p_wallet_password in varchar2); + + -- set gateway configuration + procedure set_gateway_config (p_gateway_config in t_gateway_config); + + -- send SMS message + procedure send_sms (p_message in varchar2, + p_to in varchar2, + p_from in varchar2, + p_attr1 in varchar2 := null, + p_attr2 in varchar2 := null, + p_attr3 in varchar2 := null, + p_username in varchar2 := null, + p_password in varchar2 := null); + +end sms_util_pkg; +/ + diff --git a/setup/install.sql b/setup/install.sql index 5d79921..93470c4 100755 --- a/setup/install.sql +++ b/setup/install.sql @@ -39,6 +39,7 @@ prompt Creating package specifications @../ora/raw_util_pkg.pks @../ora/regexp_util_pkg.pks @../ora/rss_util_pkg.pks +@../ora/sms_util_pkg.pks @../ora/soap_server_pkg.pks @../ora/sql_builder_pkg.pks @../ora/sql_util_pkg.pks @@ -89,6 +90,7 @@ prompt Creating package bodies @../ora/raw_util_pkg.pkb @../ora/regexp_util_pkg.pkb @../ora/rss_util_pkg.pkb +@../ora/sms_util_pkg.pkb @../ora/soap_server_pkg.pkb @../ora/sql_builder_pkg.pkb @../ora/sql_util_pkg.pkb diff --git a/setup/synonyms.sql b/setup/synonyms.sql index 39e07c0..159a59f 100755 --- a/setup/synonyms.sql +++ b/setup/synonyms.sql @@ -35,6 +35,7 @@ create synonym ax_random for random_util_pkg; create synonym ax_raw for raw_util_pkg; create synonym ax_regexp for regexp_util_pkg; create synonym ax_rss for rss_util_pkg; +create synonym ax_sms for sms_util_pkg; create synonym ax_sql_builder for sql_builder_pkg; create synonym ax_sql for sql_util_pkg; create synonym ax_string for string_util_pkg; From b557efce4c980065ded54a57876d17a5a5206fc3 Mon Sep 17 00:00:00 2001 From: Morten Braten Date: Fri, 30 Oct 2015 14:24:15 +0100 Subject: [PATCH 20/21] Added add_token procedure --- ora/string_util_pkg.pkb | 27 +++++++++++++++++++++++++++ ora/string_util_pkg.pks | 9 +++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/ora/string_util_pkg.pkb b/ora/string_util_pkg.pkb index b1b168b..3304d77 100755 --- a/ora/string_util_pkg.pkb +++ b/ora/string_util_pkg.pkb @@ -98,6 +98,33 @@ begin end get_str; +procedure add_token (p_text in out varchar2, + p_token in varchar2, + p_separator in varchar2 := g_default_separator) +as +begin + + /* + + Purpose: add token to string + + Remarks: + + Who Date Description + ------ ---------- ------------------------------------- + MBR 30.10.2015 Created + + */ + + if p_text is null then + p_text := p_token; + else + p_text := p_text || p_separator || p_token; + end if; + +end add_token; + + function get_nth_token(p_text in varchar2, p_num in number, p_separator in varchar2 := g_default_separator) return varchar2 diff --git a/ora/string_util_pkg.pks b/ora/string_util_pkg.pks index bf5667f..f901797 100755 --- a/ora/string_util_pkg.pks +++ b/ora/string_util_pkg.pks @@ -34,8 +34,8 @@ as g_tab constant varchar2(1) := chr(9); g_ampersand constant varchar2(1) := chr(38); - g_html_entity_carriage_return constant varchar2(5) := ' '; - g_html_nbsp constant varchar2(6) := ' '; + g_html_entity_carriage_return constant varchar2(5) := chr(38) || '#13;'; + g_html_nbsp constant varchar2(6) := chr(38) || 'nbsp;'; -- return string merged with substitution values function get_str (p_msg in varchar2, @@ -48,6 +48,11 @@ as p_value7 in varchar2 := null, p_value8 in varchar2 := null) return varchar2; + -- add token to string + procedure add_token (p_text in out varchar2, + p_token in varchar2, + p_separator in varchar2 := g_default_separator); + -- get the sub-string at the Nth position function get_nth_token(p_text in varchar2, p_num in number, From 04214e0844bd77992a792f0626dbfdcf53199d1e Mon Sep 17 00:00:00 2001 From: Morten Braten Date: Wed, 25 Nov 2015 18:23:05 +0100 Subject: [PATCH 21/21] Added concat_array function --- ora/string_util_pkg.pkb | 35 +++++++++++++++++++++++++++++++++++ ora/string_util_pkg.pks | 4 +++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/ora/string_util_pkg.pkb b/ora/string_util_pkg.pkb index 3304d77..d2f6229 100755 --- a/ora/string_util_pkg.pkb +++ b/ora/string_util_pkg.pkb @@ -1110,6 +1110,41 @@ begin end value_has_changed; +function concat_array (p_array in t_str_array, + p_separator in varchar2 := g_default_separator) return varchar2 +as + l_returnvalue t_max_pl_varchar2; +begin + + /* + + Purpose: concatenate non-null strings with specified separator + + Remarks: + + Who Date Description + ------ ---------- -------------------------------- + MBR 19.11.2015 Created + + */ + + if p_array.count > 0 then + for i in 1 .. p_array.count loop + if p_array(i) is not null then + if l_returnvalue is null then + l_returnvalue := p_array(i); + else + l_returnvalue := l_returnvalue || p_separator || p_array(i); + end if; + end if; + end loop; + end if; + + return l_returnvalue; + +end concat_array; + + end string_util_pkg; / diff --git a/ora/string_util_pkg.pks b/ora/string_util_pkg.pks index f901797..ab03d2d 100755 --- a/ora/string_util_pkg.pks +++ b/ora/string_util_pkg.pks @@ -164,7 +164,6 @@ as p_list in varchar2, p_separator in varchar2 := g_default_separator) return boolean; - -- randomize array function randomize_array (p_array in t_str_array) return t_str_array; @@ -172,6 +171,9 @@ as function value_has_changed (p_old in varchar2, p_new in varchar2) return boolean; + -- concatenate non-null strings with specified separator + function concat_array (p_array in t_str_array, + p_separator in varchar2 := g_default_separator) return varchar2; end string_util_pkg; /