======Overview======
Long time ago, I had to create procedure which will unlock a user, send the password to some and it's status to others and check if the user is used or not.
In a nutshell, the requirments for that procedures were:
- On (Unlock the user if it isn't used, change the password and send the password to the requested people and to others send its status)
- Off (Lock the user, if it isn't user and send mail with the status to all relevant parties)
It took me around 4 months to finish all the details of the package as a lot of new requirments were coming, but here is the final result :)
======Source Code======
CREATE OR REPLACE PACKAGE manage_fixer AS
FUNCTION Parsing(text in Varchar2) return varchar2;
FUNCTION RendrText(text IN VARCHAR2) return varchar2;
/*
Use following code when updates of RendrText required:
Update RenderText and test via Parsing_test
-- ==========================================
PROCEDURE Parsing_test(Test in Varchar2);
PROCEDURE renderText (text IN VARCHAR2);
*/
PROCEDURE SEND_MAIL(p_sender IN VARCHAR2,
p_recipient IN VARCHAR2,
p_subject IN VARCHAR2,
p_message IN VARCHAR2);
PROCEDURE SDM(p_r_from in varchar2,
text in VARCHAR2,
p_sbj in VARCHAR2,
o_message in VARCHAR2,
p_all_to in VARCHAR2,
p_all_cc in VARCHAR2);
PROCEDURE SEND_MAIL_O(p_sender IN VARCHAR2,
p_recipient IN VARCHAR2,
p_subject IN VARCHAR2,
p_message IN VARCHAR2,
recs IN VARCHAR2,
recs2 IN VARCHAR2);
PROCEDURE turnFixer (
OnOff IN VARCHAR2,
p_from IN VARCHAR2,
p_sbj IN VARCHAR2,
p_to IN VARCHAR2, -- password requestor
p_to_sec IN VARCHAR2, -- all others to be notified as TO
p_cc_sec IN VARCHAR2, -- all others to be notified as CC
p_cc_oo IN VARCHAR2 default null, -- non-standard TO e-mail addresses
DATE_WN IN VARCHAR2, -- Request timestamp
DATE_CT IN VARCHAR2, -- Date confirmed
CNFT_BY IN VARCHAR2);
END manage_fixer;
CREATE OR REPLACE PACKAGE BODY manage_fixer
AS
function Parsing(text in Varchar2) return varchar2
is
v_next_column binary_integer;
v_rec_em_len binary_integer;
v_rec_emails varchar2(2000);
v_sngl_rec_em varchar2(200);
type array is varray(100) of varchar2(2000);
array1 array := array();
Names varchar2(2000);
i Number;
begin
v_rec_emails := text;
v_rec_em_len := length(v_rec_emails);
v_rec_emails := v_rec_emails||';';
v_next_column := 1;
i := 1;
IF instr(v_rec_emails,';') = 0 THEN
v_sngl_rec_em := v_rec_emails;
v_rec_em_len := 1;
END IF;
WHILE v_next_column <= v_rec_em_len
LOOP
v_sngl_rec_em := substr(v_rec_emails,v_next_column,instr(v_rec_emails,';',v_next_column) - v_next_column);
v_next_column := instr(v_rec_emails,';',v_next_column)+1;
array1.extend;
IF trim(v_sngl_rec_em) like 'Change%' THEN
v_sngl_rec_em := 'changeManagement@company.bg';
ELSIF trim(v_sngl_rec_em) like 'FC_Adminin%' THEN
v_sngl_rec_em := 'Fc_Administrators@company.bg';
ELSIF trim(v_sngl_rec_em) like 'iFlex%' THEN
v_sngl_rec_em := 'iFlexSupportTeam@company.bg';
ELSIF trim(v_sngl_rec_em) like 'DB %' THEN
v_sngl_rec_em := 'dbadmins@company.bg';
ELSE
v_sngl_rec_em := Rendrtext(text => v_sngl_rec_em);
END IF;
array1(i) := v_sngl_rec_em||'; ';
Names := Names||array1(i);
i := i + 1;
END LOOP;
return(Names);
end Parsing;
function RendrText(text IN VARCHAR2) return varchar2
is
v_next_column binary_integer;
v_rec_em_len binary_integer;
v_rec_emails varchar2(2000);
v_sngl_rec_em varchar2(2000);
type array is varray(100) of varchar(2000);
array1 array := array();
Names varchar2(2000);
i Number;
begin
v_rec_emails := trim(text);
v_rec_em_len := length(v_rec_emails);
v_rec_emails := v_rec_emails||' ';
v_next_column := 1;
i := 1;
IF instr(v_rec_emails,' ') = 0 THEN
v_sngl_rec_em := v_rec_emails;
v_rec_em_len := 1;
END IF;
WHILE v_next_column <= v_rec_em_len
LOOP
v_sngl_rec_em := substr(v_rec_emails,v_next_column,instr(v_rec_emails,' ',v_next_column) - v_next_column);
v_next_column := instr(v_rec_emails,' ',v_next_column)+1;
array1.extend;
array1(i) := v_sngl_rec_em;
i := i + 1;
END LOOP;
dbms_output.put_line(names);
Names := array1(2)||'.'||array1(1)||'@company.bg';
return(names);
end RendrText;
/* Use following code when updates of RendrText required:
Update RenderText and test via Parsing_test
-- ==========================================
procedure Parsing_test(Test in Varchar2) is
v_next_column binary_integer;
v_rec_em_len binary_integer;
v_rec_emails varchar2(2000);
v_sngl_rec_em varchar2(2000);
type array is varray(100) of varchar(2000);
array1 array := array();
Names varchar2(2000);
i Number;
begin
v_rec_emails := Test;
v_rec_em_len := length(v_rec_emails);
v_rec_emails := v_rec_emails||';';
v_next_column := 1;
i := 1;
IF instr(v_rec_emails,';') = 0 THEN
v_sngl_rec_em := v_rec_emails;
v_rec_em_len := 1;
END IF;
WHILE v_next_column <= v_rec_em_len
LOOP
v_sngl_rec_em := substr(v_rec_emails,v_next_column,instr(v_rec_emails,';',v_next_column) - v_next_column);
v_next_column := instr(v_rec_emails,';',v_next_column)+1;
array1.extend;
IF trim(v_sngl_rec_em) like 'Change Management (MG)' THEN
v_sngl_rec_em := 'changeManagement@company.bg';
ELSIF trim(v_sngl_rec_em) like 'FC_Administrators' THEN
v_sngl_rec_em := 'Fc_Administrators@company.bg';
ELSIF trim(v_sngl_rec_em) like 'iFlex Support Team' THEN
v_sngl_rec_em := 'iFlexSupportTeam@company.bg';
ELSIF trim(v_sngl_rec_em) like 'DB Admins (MG)' THEN
v_sngl_rec_em := 'dbadmins@company.bg';
ELSE
v_sngl_rec_em := Rendrtext(text => v_sngl_rec_em);
END IF;
dbms_output.put_line(v_sngl_rec_em);
array1(i) := v_sngl_rec_em||'; ';
Names := Names||array1(i);
dbms_output.put_line(Names);
i := i + 1;
END LOOP;
dbms_output.put_line(Names);
end Parsing_test;
PROCEDURE renderText (text IN VARCHAR2)
IS
v_next_column binary_integer;
v_rec_em_len binary_integer;
v_rec_emails varchar2(2000);
v_sngl_rec_em varchar2(2000);
type array is varray(100) of varchar(2000);
array1 array := array();
Names varchar2(2000);
i Number;
BEGIN
v_rec_emails := trim(text);
v_rec_em_len := length(v_rec_emails);
v_rec_emails := v_rec_emails||' ';
v_next_column := 1;
i := 1;
IF instr(v_rec_emails,' ') = 0 THEN
v_sngl_rec_em := v_rec_emails;
v_rec_em_len := 1;
END IF;
WHILE v_next_column <= v_rec_em_len
LOOP
v_sngl_rec_em := substr(v_rec_emails,v_next_column,instr(v_rec_emails,' ',v_next_column) - v_next_column);
v_next_column := instr(v_rec_emails,' ',v_next_column)+1;
array1.extend;
array1(i) := v_sngl_rec_em;
i := i + 1;
END LOOP;
Names := array1(2)||'.'||array1(1)||'@company.bg';
dbms_output.put_line(names);
END renderText;
*/
PROCEDURE SEND_MAIL
-- Used for password requestor/recipient
( p_sender IN VARCHAR2,
p_recipient IN VARCHAR2,
p_subject IN VARCHAR2,
p_message IN VARCHAR2)
as
l_mailhost VARCHAR2(255) := 'mail.company.bg';
l_sender_pref VARCHAR2(500);
l_mail_conn utl_smtp.connection;
PROCEDURE send_header(name IN VARCHAR2, header IN VARCHAR2) AS
BEGIN
utl_smtp.write_raw_data(l_mail_conn,utl_raw.cast_to_raw(name || ': ' || header || utl_tcp.CRLF));
-- ------------------- ------------
-- ^ ca aa eceeca ee?eeeoa a subject-a ^
END;
BEGIN
select rtrim(i.host_name)||':'|| rtrim(i.instance_name)||':'||rtrim(i.instance_number)||':'||user
into l_sender_pref
from v$instance i;
l_mail_conn := utl_smtp.open_connection(l_mailhost, 25);
utl_smtp.helo(l_mail_conn, 'mail.company.bg');
utl_smtp.mail(l_mail_conn, p_sender);
utl_smtp.rcpt(l_mail_conn, p_recipient);
utl_smtp.open_data(l_mail_conn );
send_header('From', p_sender);
send_header('To: ', p_recipient);
send_header('Subject', p_subject);
send_header('Cc: ',p_recipient);
utl_smtp.write_raw_data(l_mail_conn,utl_raw.cast_to_raw(utl_tcp.CRLF || p_message || utl_tcp.CRLF));
-- ------------------ ------------
-- ^ ca aa eceeca ee?eeeoa a subject-a ^
utl_smtp.close_data(l_mail_conn );
utl_smtp.quit(l_mail_conn);
end SEND_MAIL;
PROCEDURE SEND_MAIL_O (p_sender IN VARCHAR2,
-- Used for all other e-mailing
p_recipient IN VARCHAR2,
p_subject IN VARCHAR2,
p_message IN VARCHAR2,
recs IN VARCHAR2,
recs2 IN VARCHAR2)
as
l_mailhost VARCHAR2(255) := 'mail.company.bg';
l_sender_pref VARCHAR2(255);
l_mail_conn utl_smtp.connection;
PROCEDURE send_header(name IN VARCHAR2, header IN VARCHAR2) AS
BEGIN
utl_smtp.write_raw_data(l_mail_conn,utl_raw.cast_to_raw (name || ': ' || header || utl_tcp.CRLF));
END;
BEGIN
select rtrim(i.host_name)||':'|| rtrim(i.instance_name)||':'||rtrim(i.instance_number)||':'||user
-- ------------------- ------------
-- ^ ca aa eceeca ee?eeeoa a subject-a ^
into l_sender_pref
from v$instance i;
l_mail_conn := utl_smtp.open_connection(l_mailhost, 25);
utl_smtp.helo(l_mail_conn, 'mail.company.bg');
utl_smtp.mail(l_mail_conn, p_sender);
utl_smtp.rcpt(l_mail_conn, p_recipient);
utl_smtp.open_data(l_mail_conn );
send_header('From: ',p_sender);
send_header('To: ',recs);
send_header('Subject', p_subject);
send_header('Cc: ',recs2);
utl_smtp.write_raw_data(l_mail_conn,utl_raw.cast_to_raw(utl_tcp.CRLF || p_message || utl_tcp.CRLF));
-- ------------------- ------------
-- ^ ca aa eceeca ee?eeeoa a subject-a ^
utl_smtp.close_data(l_mail_conn );
utl_smtp.quit(l_mail_conn);
end SEND_MAIL_O;
PROCEDURE SDM(p_r_from in varchar2,
-- Used for nonstandard e-mail addresses
-- They must be semi-colon separated! (Except the last one)
text in VARCHAR2,
p_sbj in VARCHAR2,
o_message in VARCHAR2,
p_all_to in VARCHAR2,
p_all_cc in VARCHAR2)
IS
v_next_column binary_integer;
v_rec_em_len binary_integer;
v_rec_emails varchar2(2000);
v_sngl_rec_em varchar2(2000);
BEGIN
v_rec_emails := trim(text);
v_rec_em_len := length(v_rec_emails);
v_rec_emails := v_rec_emails||';';
v_next_column := 1;
IF instr(v_rec_emails,';') = 0 THEN
v_sngl_rec_em := v_rec_emails;
v_rec_em_len := 1;
END IF;
WHILE v_next_column <= v_rec_em_len
LOOP
v_sngl_rec_em := substr(v_rec_emails,v_next_column,instr(v_rec_emails,';',v_next_column) - v_next_column);
v_next_column := instr(v_rec_emails,';',v_next_column)+1;
send_mail_O(p_sender => p_r_from,p_recipient => v_sngl_rec_em,p_subject => p_sbj,p_message => o_message,recs => p_all_to,recs2 => p_all_cc);
END LOOP;
END SDM;
PROCEDURE turnFixer (
OnOff IN VARCHAR2,
p_from IN VARCHAR2,
p_sbj IN VARCHAR2,
p_to IN VARCHAR2, -- password requestor
p_to_sec IN VARCHAR2, -- all others to be notified as TO
p_cc_sec IN VARCHAR2, -- all others to be notified as CC
p_cc_oo IN VARCHAR2, -- non-standard TO e-mail addresses
DATE_WN IN VARCHAR2, -- Request timestamp
DATE_CT IN VARCHAR2, -- Date confirmed
CNFT_BY IN VARCHAR2) -- Confirmator
IS
cnt INTEGER;
sts INTEGER; -- 0 means Locked, 1 means Unlocked
pwd VARCHAR2(7);
p_msg VARCHAR2(35);
o_message VARCHAR2(500);
w_message VARCHAR2(500);
c_message VARCHAR2(500);
p_r_to VARCHAR2(2000);
p_r_from VARCHAR2(2000);
v_sngl_rec_em varchar2(2000);
p_all_mails VARCHAR2(2000);
p_all_to VARCHAR2(2000);
p_all_cc VARCHAR2(2000);
p_to1 VARCHAR2(2000);
v_rec_emails varchar2(2000);
DATE_CT_N DATE;
DATE_TG DATE;
v_next_column binary_integer;
v_rec_em_len binary_integer;
BEGIN
dbms_output.put_line('Password requested at: '||DATE_WN);
p_all_to := Parsing(text => p_to_sec);
p_all_cc := Parsing(text => p_cc_sec);
p_to1 := p_to_sec ||';'|| p_cc_sec;
v_rec_emails := p_to1;
v_rec_em_len := length(v_rec_emails);
v_rec_emails := v_rec_emails||';';
p_r_to := p_to;
p_r_to := Rendrtext(text => p_r_to);
p_r_from := p_from;
p_r_from := Rendrtext(text => p_r_from);
v_next_column := 1;
o_message := 'The Fixer''s pasword was sent';
w_message := 'Please, logoff FIXER';
c_message := 'The Fixer was locked';
DATE_TG := to_date(DATE_WN,'DD.MM.YYYY HH24:MI:SS');
DATE_CT_N := to_date(DATE_CT,'DD.MM.YYYY HH24:MI:SS');
EXECUTE IMMEDIATE 'LOCK TABLE FIXER_STATUS IN EXCLUSIVE MODE';
IF instr(v_rec_emails,';') = 0 THEN
v_sngl_rec_em := v_rec_emails;
v_rec_em_len := 1;
END IF;
SELECT COUNT(*)
INTO cnt
FROM gv$session
WHERE username='FIXER';
-- Check Fixer_status table is correct, in case anybody has altered account status manually
-- update Fixer_Status
-- set status = (select DECODE(account_status,'LOCKED',0,1) from dba_users where username='FIXER');
SELECT status
INTO sts
FROM Fixer_Status;
CASE upper(OnOff)
WHEN 'ON' THEN
BEGIN
IF cnt <> 0 THEN
dbms_output.put_line('The FIXER account is already in use. Check with your colleagues!');
ELSIF sts = 1 THEN
dbms_output.put_line('The password was already sent');
ELSIF sts = 0 and cnt = 0 THEN
-- ^ keep this! ^
pwd := dbms_random.string(null,7);
p_msg := 'The new Fixer''s password is '||pwd;
EXECUTE IMMEDIATE 'alter user FIXER identified by '||pwd;
EXECUTE IMMEDIATE 'alter user FIXER account unlock';
EXECUTE IMMEDIATE 'Update fixer_status set status = 1';
EXECUTE IMMEDIATE 'commit';
-- Send the password ==================
send_mail(p_sender => p_r_from,p_recipient => p_r_to,p_subject => p_sbj,p_message => p_msg);
-- ====================================
dbms_output.put_line('E-mail to '||p_r_to||' with the password was sent');
EXECUTE IMMEDIATE 'Insert into fixer_history(N,Incident#,Incident_received_at,Account_which_is_requested,Account_requested_by,Account_requested_at,Confirmed_by,Confirmed_at,Account_enabled_by,Account_enabled_at) values(fixer.nextval,:1,:2,:3,:4,:5,:6,:7,:8,:9)'
using p_sbj,DATE_TG,'FIXER@FIXER',p_to,DATE_TG,CNFT_BY,DATE_CT_N,p_from,sysdate;
EXECUTE IMMEDIATE 'commit';
dbms_output.put_line('The history was applied');
-- Notify all others =======================================
WHILE v_next_column <= v_rec_em_len
LOOP
v_sngl_rec_em := substr(v_rec_emails,v_next_column,instr(v_rec_emails,';',v_next_column) - v_next_column);
v_next_column := instr(v_rec_emails,';',v_next_column)+1;
IF trim(v_sngl_rec_em) like 'Change%' THEN
v_sngl_rec_em := 'changeManagement@company.bg';
ELSIF trim(v_sngl_rec_em) like 'FC_Admin%' THEN
v_sngl_rec_em := 'Fc_Administrators@company.bg';
ELSIF trim(v_sngl_rec_em) like 'iFlex%' THEN
v_sngl_rec_em := 'iFlexSupportTeam@company.bg';
ELSIF trim(v_sngl_rec_em) like 'DB%' THEN
v_sngl_rec_em := 'dbadmins@company.bg';
ELSE
v_sngl_rec_em := Rendrtext(text => v_sngl_rec_em);
END IF;
send_mail_O(p_sender => p_r_from,p_recipient => v_sngl_rec_em,p_subject => p_sbj,p_message => o_message,recs => p_all_to,recs2 => p_all_cc);
p_all_mails := v_sngl_rec_em ||' and '||p_all_mails;
END LOOP;
dbms_output.put_line('Confirmation e-mail was sent to '||p_all_mails||' and '||p_cc_oo);
-- =========================================================
-- Notify non-standard e-mails >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IF p_cc_oo is not null THEN
SDM(p_r_from,p_cc_oo,p_sbj,o_message,p_all_to,p_all_cc);
END IF;
-- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ELSE
dbms_output.put_line('Error, please check the fixer''s table. Sts='||sts||' cnt='||cnt);
-- Should never come here!
END IF;
END;
WHEN 'OFF' THEN
BEGIN
IF cnt <> 0 THEN
send_mail(p_sender => p_r_from,p_recipient => p_r_to,p_subject => p_sbj,p_message => w_message);
-- Maybe send this e-mail to others
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
dbms_output.put_line('The FIXER account is in use and isn''t locked please try again after some time. Email to '||p_to||' with logoff request was sent.');
ELSIF sts = 0 THEN
dbms_output.put_line('The Fixer was already locked');
ELSIF sts = 1 and cnt = 0 THEN
EXECUTE IMMEDIATE 'alter user fixer account lock';
EXECUTE IMMEDIATE 'update fixer_status set status = 0';
EXECUTE IMMEDIATE 'commit';
send_mail(p_sender => p_r_from,p_recipient => p_r_to,p_subject => p_sbj,p_message => c_message);
dbms_output.put_line('E-mail to '||p_r_to||' with locking information was sent');
dbms_output.put_line('Update fixer_history set ACCOUNT_DISABLED_BY ='''||p_from||''',ACCOUNT_DISABLED_AT = '''||sysdate||''' where INCIDENT# = '''||p_sbj||''';');
EXECUTE IMMEDIATE 'Update fixer_history set ACCOUNT_DISABLED_BY ='''||p_from||''',ACCOUNT_DISABLED_AT = sysdate where INCIDENT# = '''||p_sbj||'''';
EXECUTE IMMEDIATE 'commit';
dbms_output.put_line('The history was applied');
WHILE v_next_column <= v_rec_em_len
LOOP
v_sngl_rec_em := substr(v_rec_emails,v_next_column,instr(v_rec_emails,';',v_next_column) - v_next_column);
v_next_column := instr(v_rec_emails,';',v_next_column)+1;
IF trim(v_sngl_rec_em) like 'Change%' THEN
v_sngl_rec_em := 'changeManagement@company.bg';
ELSIF trim(v_sngl_rec_em) like 'FC_Adminin%' THEN
v_sngl_rec_em := 'Fc_Administrators@company.bg';
ELSIF trim(v_sngl_rec_em) like 'iFlex%' THEN
v_sngl_rec_em := 'iFlexSupportTeam@company.bg';
ELSIF trim(v_sngl_rec_em) like 'DB%' THEN
v_sngl_rec_em := 'dbadmins@company.bg';
ELSE
v_sngl_rec_em := Rendrtext(text => v_sngl_rec_em);
END IF;
send_mail_O(p_sender => p_r_from,p_recipient => v_sngl_rec_em,p_subject => p_sbj,p_message => c_message,recs => p_all_to,recs2 => p_all_cc);
p_all_mails := v_sngl_rec_em ||' and '||p_all_mails;
END LOOP;
dbms_output.put_line('The locking e-mail was sent to '||p_all_mails||' and '||p_cc_oo);
IF p_cc_oo is not null THEN
SDM(p_r_from,p_cc_oo,p_sbj,c_message,p_all_to,p_all_cc);
END IF;
ELSE
dbms_output.put_line('Error, please check the fixer''s table');
END IF;
END;
END CASE;
END turnFixer;
END manage_fixer;
======Usage======
That package is called as follows:
store set origenv replace
set longchunksize 300
set feed on;
set serveroutput on
set ver on;
prompt Enter values:
prompt ============
prompt for OnOrOff - OnOff
prompt for From - p_from
prompt for Subject - p_sbj
prompt for To - p_to
prompt for MAIL_ADDRESSES_IN_CC - p_to_sec
prompt for MAIL_ADDRESSES_IN_BB - p_cc_sec
prompt for DIFFERENT_MAIL_ADDRESSES - p_cc_oo
prompt for DATE_WNT - DATE_WN
prompt for DATE_CM - DATE_CT
prompt for CNF_BY - CNFT_BY
define ON_OR_OFF =&OnOrOff
define FRM =&From
define SBJ =&Subject
define TOT =&To
define TOOTH =&MAIL_ADDRESSES_IN_CC
define CCOTH =&MAIL_ADDRESSES_IN_BB
define CCOO =&DIFFERENT_MAIL_ADDRESSES
define DTGT =&DATE_WNT
define DTCT =&DATE_CM
define CNFT =&CNF_BY
col xx format a400
exec MANAGE_FIXER_2.turnfixer(OnOff => '&ON_OR_OFF',p_from => '&FRM',p_sbj => '&SBJ',p_to => '&TOT',p_to_sec =>'&TOOTH',p_cc_sec =>'&CCOTH',p_cc_oo => '&CCOO',DATE_WN => '&DTGT', DATE_CT => '&DTCT',CNFT_BY => '&CNFT');
@origenv
undef On_Or_Off
undef Frm
undef sbj
undef To
Undef ToOth
undef CcOth
undef CCOO
undef DTGT
undef DTCT
undef CNFT