diff options
author | Aleksander Machniak <machniak@kolabsys.com> | 2014-09-19 13:11:53 +0200 |
---|---|---|
committer | Aleksander Machniak <machniak@kolabsys.com> | 2014-09-19 13:11:53 +0200 |
commit | 281cb9f4ef6a5f7a3e3fb707510dc2ad225ef3ef (patch) | |
tree | 815c89eabd5fd6bb40a0cc971705e14866c3dceb | |
parent | b8f70c69d9a2a5e2ab7f42ab08fe95696236e13d (diff) | |
download | webadmin-oracle.tar.gz |
Added Oracle supportoracle
-rw-r--r-- | doc/kolab_wap.oracle.sql | 215 | ||||
-rw-r--r-- | lib/SQL.php | 61 | ||||
-rw-r--r-- | lib/SQL/oracle.php | 467 | ||||
-rw-r--r-- | lib/api/kolab_api_service_form_value.php | 2 | ||||
-rw-r--r-- | lib/api/kolab_api_service_type.php | 14 | ||||
-rw-r--r-- | lib/kolab_api_service.php | 2 |
6 files changed, 746 insertions, 15 deletions
diff --git a/doc/kolab_wap.oracle.sql b/doc/kolab_wap.oracle.sql new file mode 100644 index 0000000..f08d89c --- /dev/null +++ b/doc/kolab_wap.oracle.sql @@ -0,0 +1,215 @@ + +CREATE TABLE "group_types" ( + "id" integer PRIMARY KEY, + "key" varchar(256) NOT NULL, + "name" varchar(256) NOT NULL, + "description" varchar(2048) DEFAULT NULL, + "attributes" long NOT NULL, + "is_default" smallint DEFAULT 0 +); + +CREATE UNIQUE INDEX "group_types_name_idx" ON "group_types" ("name"); + +CREATE SEQUENCE "group_types_seq" + START WITH 1 INCREMENT BY 1 NOMAXVALUE; + +CREATE OR REPLACE TRIGGER "group_types_seq_trig" +BEFORE INSERT ON "group_types" FOR EACH ROW +BEGIN + IF :NEW."id" IS NULL THEN + :NEW."id" := "group_types_seq".nextval; + END IF; +END; + +INSERT ALL + INTO "group_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (1,'kolab','Kolab Distribution Group (Static)','A static Kolab Distribution Group (with mail address)','{"auto_form_fields":{"mail":{"data":["cn"]}},"fields":{"objectclass":["top","groupofuniquenames","kolabgroupofuniquenames"]},"form_fields":{"cn":[],"kolaballowsmtprecipient":{"type":"list","optional":true},"kolaballowsmtpsender":{"type":"list","optional":true},"ou":{"type":"select"},"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}',0) + INTO "group_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (2,'kolab_dynamic','Kolab Distribution Group (Dynamic)','A dynamic Kolab Distribution Group (with mail address)','{"auto_form_fields":{"mail":{"data":["cn"]}},"fields":{"objectclass":["top","groupofurls","kolabgroupofuniquenames"]},"form_fields":{"cn":[],"kolaballowsmtprecipient":{"type":"list","optional":true},"kolaballowsmtpsender":{"type":"list","optional":true},"memberurl":{"type":"ldap_url","optional":true},"ou":{"type":"select"},"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}',0) + INTO "group_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (3,'posix','(Pure) POSIX Group','A pure UNIX POSIX Group','{"auto_form_fields":{"gidnumber":[]},"fields":{"objectclass":["top","groupofuniquenames","posixgroup"]},"form_fields":{"cn":[],"ou":{"type":"select"},"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}',0) + INTO "group_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (4,'posix_mail','Mail-enabled POSIX Group','A Kolab and also UNIX POSIX Group','{"auto_form_fields":{"gidnumber":[],"mail":{"data":["cn"]}},"fields":{"objectclass":["top","groupofuniquenames","kolabgroupofuniquenames","posixgroup"]},"form_fields":{"cn":[],"kolaballowsmtprecipient":{"type":"list","optional":true},"kolaballowsmtpsender":{"type":"list","optional":true},"mail":{"optional":true},"ou":{"type":"select"},"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}',0) + INTO "group_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (5,'simple','Simple Group (Static)','A simple, traditional LDAP group with a static list of members','{"auto_form_fields":[],"fields":{"objectclass":["top","groupofuniquenames"]},"form_fields":{"cn":[],"ou":{"type":"select"},"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}',0) +SELECT 1 FROM DUAL; + + +CREATE TABLE "options" ( + "attribute" varchar(128) NOT NULL, + "option_values" long NOT NULL +); + +INSERT ALL + INTO "options" ("attribute", "option_values") + VALUES ('preferredlanguage','["aa_DJ","aa_ER","aa_ET","af_ZA","am_ET","an_ES","ar_AE","ar_BH","ar_DZ","ar_EG","ar_IN","ar_IQ","ar_JO","ar_KW","ar_LB","ar_LY","ar_MA","ar_OM","ar_QA","ar_SA","ar_SD","ar_SY","ar_TN","ar_YE","as_IN","az_AZ","be_BY","bg_BG","bn_BD","bn_IN","bokmal","br_FR","bs_BA","byn_ER","C","ca_AD","ca_ES","ca_FR","ca_IT","catalan","croatian","csb_PL","cs_CZ","cy_GB","czech","da_DK","danish","dansk","de_AT","de_BE","de_CH","de_DE","de_LU","deutsch","dutch","dz_BT","eesti","el_CY","el_GR","en_AU","en_BW","en_CA","en_DK","en_GB","en_HK","en_IE","en_IN","en_NZ","en_PH","en_SG","en_US","en_ZA","en_ZW","es_AR","es_BO","es_CL","es_CO","es_CR","es_DO","es_EC","es_ES","es_GT","es_HN","es_MX","es_NI","es_PA","es_PE","es_PR","es_PY","es_SV","estonian","es_US","es_UY","es_VE","et_EE","eu_ES","fa_IR","fi_FI","finnish","fo_FO","fr_BE","fr_CA","fr_CH","french","fr_FR","fr_LU","fy_NL","ga_IE","galego","galician","gd_GB","german","gez_ER","gez_ET","gl_ES","greek","gu_IN","gv_GB","hebrew","he_IL","hi_IN","hr_HR","hrvatski","hsb_DE","hu_HU","hungarian","hy_AM","icelandic","id_ID","is_IS","italian","it_CH","it_IT","iw_IL","ja_JP","japanese","ka_GE","kk_KZ","kl_GL","km_KH","kn_IN","ko_KR","korean","ku_TR","kw_GB","ky_KG","lg_UG","lithuanian","lo_LA","lt_LT","lv_LV","mai_IN","mg_MG","mi_NZ","mk_MK","ml_IN","mn_MN","mr_IN","ms_MY","mt_MT","nb_NO","ne_NP","nl_BE","nl_NL","nn_NO","no_NO","norwegian","nr_ZA","nso_ZA","nynorsk","oc_FR","om_ET","om_KE","or_IN","pa_IN","pa_PK","pl_PL","polish","portuguese","POSIX","pt_BR","pt_PT","romanian","ro_RO","ru_RU","russian","ru_UA","rw_RW","se_NO","sid_ET","si_LK","sk_SK","slovak","slovene","slovenian","sl_SI","so_DJ","so_ET","so_KE","so_SO","spanish","sq_AL","sr_CS","sr_ME","sr_RS","ss_ZA","st_ZA","sv_FI","sv_SE","swedish","ta_IN","te_IN","tg_TJ","thai","th_TH","ti_ER","ti_ET","tig_ER","tl_PH","tn_ZA","tr_CY","tr_TR","ts_ZA","tt_RU","turkish","uk_UA","ur_PK","uz_UZ","ve_ZA","vi_VN","wa_BE","xh_ZA","yi_US","zh_CN","zh_HK","zh_SG","zh_TW","zu_ZA"]') + INTO "options" ("attribute", "option_values") + VALUES ('c','["AD","AE","AF","AG","AI","AL","AM","AO","AQ","AR","AS","AT","AU","AW","AX","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","GB","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","YU","ZA","ZM","ZW"]') +SELECT 1 FROM DUAL; + + +CREATE TABLE "ou_types" ( + "id" integer PRIMARY KEY, + "key" varchar(256) NOT NULL, + "name" varchar(256) NOT NULL, + "description" varchar(2048) DEFAULT NULL, + "attributes" long NOT NULL, + "is_default" smallint DEFAULT 0 +); + +CREATE UNIQUE INDEX "ou_types_name_idx" ON "ou_types" ("name"); + +CREATE SEQUENCE "ou_types_seq" + START WITH 1 INCREMENT BY 1 NOMAXVALUE; + +CREATE OR REPLACE TRIGGER "ou_types_seq_trig" +BEFORE INSERT ON "ou_types" FOR EACH ROW +BEGIN + IF :NEW."id" IS NULL THEN + :NEW."id" := "ou_types_seq".nextval; + END IF; +END; + +INSERT ALL + INTO "ou_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (1,'unit','Standard Organizational Unit','A standard organizational unit definition','{"auto_form_fields":[],"fields":{"objectclass":["top","organizationalunit"]},"form_fields":{"ou":[],"description":[],"aci":{"optional":true,"type":"aci"}}}',0) +SELECT 1 FROM DUAL; + + +CREATE TABLE "resource_types" ( + "id" integer PRIMARY KEY, + "key" varchar(256) NOT NULL, + "name" varchar(256) NOT NULL, + "description" varchar(2048) DEFAULT NULL, + "attributes" long NOT NULL, + "is_default" smallint DEFAULT 0 +); + +CREATE UNIQUE INDEX "resource_types_name_idx" ON "resource_types" ("name"); + +CREATE SEQUENCE "resource_types_seq" + START WITH 1 INCREMENT BY 1 NOMAXVALUE; + +CREATE OR REPLACE TRIGGER "resource_types_seq_trig" +BEFORE INSERT ON "resource_types" FOR EACH ROW +BEGIN + IF :NEW."id" IS NULL THEN + :NEW."id" := "resource_types_seq".nextval; + END IF; +END; + +INSERT ALL + INTO "resource_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (1,'collection','Resource Collection','A collection or pool of resources','{"auto_form_fields":{"mail":{"data":["cn"]}},"fields":{"objectclass":["top","groupofuniquenames","kolabgroupofuniquenames"]},"form_fields":{"cn":[],"ou":{"type":"select"},"uniquemember":{"type":"list","autocomplete":true,"optional":true}}}',0) + INTO "resource_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (2,'car','Car','A car','{"auto_form_fields":{"cn":{"data":["cn"]},"kolabtargetfolder":{"data":["cn"]},"mail":{"data":["cn"]}},"fields":{"objectclass":["top","kolabsharedfolder","mailrecipient"],"kolabfoldertype":["event"]},"form_fields":{"acl":{"type":"imap_acl","optional":true},"cn":[],"ou":{"type":"select"},"owner":{"type":"list","autocomplete":true,"optional":true}}}',0) + INTO "resource_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (3,'confroom','Conference Room','A conference room','{"auto_form_fields":{"cn":{"data":["cn"]},"kolabtargetfolder":{"data":["cn"]},"mail":{"data":["cn"]}},"fields":{"objectclass":["top","kolabsharedfolder","mailrecipient"],"kolabfoldertype":["event"]},"form_fields":{"acl":{"type":"imap_acl","optional":true},"cn":[],"ou":{"type":"select"},"owner":{"type":"list","autocomplete":true,"optional":true}}}',0) + INTO "resource_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (4,'projector','Projector','A portable overhead projector','{"auto_form_fields":{"cn":{"data":["cn"]},"kolabtargetfolder":{"data":["cn"]},"mail":{"data":["cn"]}},"fields":{"objectclass":["top","kolabsharedfolder","mailrecipient"],"kolabfoldertype":["event"]},"form_fields":{"acl":{"type":"imap_acl","optional":true},"cn":[],"ou":{"type":"select"},"owner":{"type":"list","autocomplete":true,"optional":true}}}',0) + INTO "resource_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (5,'footballtickets','Football Season Tickets','Season tickets to the game (pretty good seats too!)','{"auto_form_fields":{"cn":{"data":["cn"]},"kolabtargetfolder":{"data":["cn"]},"mail":{"data":["cn"]}},"fields":{"objectclass":["top","kolabsharedfolder","mailrecipient"],"kolabfoldertype":["event"]},"form_fields":{"acl":{"type":"imap_acl","optional":true},"cn":[],"ou":{"type":"select"},"owner":{"type":"list","autocomplete":true,"optional":true}}}',0) +SELECT 1 FROM DUAL; + + +CREATE TABLE "role_types" ( + "id" integer PRIMARY KEY, + "key" varchar(256) NOT NULL, + "name" varchar(256) NOT NULL, + "description" varchar(2048) DEFAULT NULL, + "attributes" long NOT NULL, + "is_default" smallint DEFAULT 0 +); + +CREATE UNIQUE INDEX "role_types_name_idx" ON "role_types" ("name"); + +CREATE SEQUENCE "role_types_seq" + START WITH 1 INCREMENT BY 1 NOMAXVALUE; + +CREATE OR REPLACE TRIGGER "role_types_seq_trig" +BEFORE INSERT ON "role_types" FOR EACH ROW +BEGIN + IF :NEW."id" IS NULL THEN + :NEW."id" := "role_types_seq".nextval; + END IF; +END; + +INSERT ALL + INTO "role_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (1,'simple_managed','Standard Role','A standard role definition','{"auto_form_fields":[],"fields":{"objectclass":["top","ldapsubentry","nsroledefinition","nssimpleroledefinition","nsmanagedroledefinition"]},"form_fields":{"cn":[],"description":[]}}',0) +SELECT 1 FROM DUAL; + +CREATE TABLE "sharedfolder_types" ( + "id" integer PRIMARY KEY, + "key" varchar(256) NOT NULL, + "name" varchar(256) NOT NULL, + "description" varchar(2048) DEFAULT NULL, + "attributes" long NOT NULL, + "is_default" smallint DEFAULT 0 +); + +CREATE UNIQUE INDEX "sharedfolder_types_name_idx" ON "sharedfolder_types" ("name"); + +CREATE SEQUENCE "sharedfolder_types_seq" + START WITH 1 INCREMENT BY 1 NOMAXVALUE; + +CREATE OR REPLACE TRIGGER "sharedfolder_types_seq_trig" +BEFORE INSERT ON "sharedfolder_types" FOR EACH ROW +BEGIN + IF :NEW."id" IS NULL THEN + :NEW."id" := "sharedfolder_types_seq".nextval; + END IF; +END; + +INSERT ALL + INTO "sharedfolder_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (1,'addressbook','Shared Address Book','A shared address book','{"auto_form_fields":[],"fields":{"kolabfoldertype":["contact"],"objectclass":["top","kolabsharedfolder"]},"form_fields":{"acl":{"type":"imap_acl","optional":true,"default":"anyone, lrs"},"cn":[]}}',0) + INTO "sharedfolder_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (2,'calendar','Shared Calendar','A shared calendar','{"auto_form_fields":[],"fields":{"kolabfoldertype":["event"],"objectclass":["top","kolabsharedfolder"]},"form_fields":{"acl":{"type":"imap_acl","optional":true,"default":"anyone, lrs"},"cn":[]}}',0) + INTO "sharedfolder_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (3,'journal','Shared Journal','A shared journal','{"auto_form_fields":[],"fields":{"kolabfoldertype":["journal"],"objectclass":["top","kolabsharedfolder"]},"form_fields":{"acl":{"type":"imap_acl","optional":true,"default":"anyone, lrs"},"cn":[]}}',0) + INTO "sharedfolder_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (4,'task','Shared Tasks','A shared tasks folder','{"auto_form_fields":[],"fields":{"kolabfoldertype":["task"],"objectclass":["top","kolabsharedfolder"]},"form_fields":{"acl":{"type":"imap_acl","optional":true,"default":"anyone, lrs"},"cn":[]}}',0) + INTO "sharedfolder_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (5,'note','Shared Notes','A shared Notes folder','{"auto_form_fields":[],"fields":{"kolabfoldertype":["note"],"objectclass":["top","kolabsharedfolder"]},"form_fields":{"acl":{"type":"imap_acl","optional":true,"default":"anyone, lrs"},"cn":[]}}',0) + INTO "sharedfolder_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (6,'file','Shared Files','A shared Files folder','{"auto_form_fields":[],"fields":{"kolabfoldertype":["file"],"objectclass":["top","kolabsharedfolder"]},"form_fields":{"acl":{"type":"imap_acl","optional":true,"default":"anyone, lrs"},"cn":[]}}',0) + INTO "sharedfolder_types" ("id", "key", "name", "description", "attributes", "is_default") + VALUES (7,'mail','Shared Mail Folder','A shared mail folder','{"auto_form_fields":[],"fields":{"kolabfoldertype":["mail"],"objectclass":["top","kolabsharedfolder","mailrecipient"]},"form_fields":{"acl":{"type":"imap_acl","optional":true,"default":"anyone, lrs"},"cn":[],"alias":{"type":"list","optional":true},"kolabdelegate":{"type":"list","autocomplete":true,"optional":true},"kolaballowsmtprecipient":{"type":"list","optional":true},"kolaballowsmtpsender":{"type":"list","optional":true},"kolabtargetfolder":[],"mail":[]}}',0) +SELECT 1 FROM DUAL; + +CREATE TABLE "user_types" ( + "id" integer PRIMARY KEY, + "key" varchar(256) NOT NULL, + "name" varchar(256) NOT NULL, + "description" varchar(2048) DEFAULT NULL, + "attributes" long NOT NULL, + "used_for" varchar(16) DEFAULT NULL, + "is_default" smallint DEFAULT 0 +); + +CREATE UNIQUE INDEX "user_types_name_idx" ON "user_types" ("name"); + +CREATE SEQUENCE "user_types_seq" + START WITH 1 INCREMENT BY 1 NOMAXVALUE; + +CREATE OR REPLACE TRIGGER "user_types_seq_trig" +BEFORE INSERT ON "user_types" FOR EACH ROW +BEGIN + IF :NEW."id" IS NULL THEN + :NEW."id" := "user_types_seq".nextval; + END IF; +END; + +INSERT ALL + INTO "user_types" ("id", "key", "name", "description", "attributes", "used_for", "is_default") + VALUES (1,'kolab','Kolab User','A Kolab User','{"auto_form_fields":{"alias":{"type":"list","data":["givenname","preferredlanguage","sn"]},"cn":{"data":["givenname","sn"]},"displayname":{"data":["givenname","sn"]},"mail":{"data":["givenname","preferredlanguage","sn"]},"mailhost":{"optional":true},"uid":{"data":["givenname","preferredlanguage","sn"]},"userpassword":{"optional":true}},"form_fields":{"alias":{"optional":true},"givenname":[],"initials":{"optional":true},"kolabdelegate":{"type":"list","autocomplete":true,"optional":true},"kolabinvitationpolicy":{"type":"select","values":["","ACT_MANUAL","ACT_REJECT"],"optional":true},"kolaballowsmtprecipient":{"type":"list","optional":true},"kolaballowsmtpsender":{"type":"list","optional":true},"l":{"optional":true},"mailalternateaddress":{"type":"list","optional":true},"mailquota":{"type":"text-quota","optional":true},"mobile":{"optional":true},"nsroledn":{"type":"list","autocomplete":true,"optional":true},"o":{"optional":true},"ou":{"type":"select"},"pager":{"optional":true},"postalcode":{"optional":true},"preferredlanguage":{"type":"select"},"sn":[],"street":{"optional":true},"telephonenumber":{"optional":true},"title":{"optional":true},"userpassword":{"optional":true}},"fields":{"objectclass":["top","inetorgperson","kolabinetorgperson","mailrecipient","organizationalperson","person"]}}',NULL,1) + INTO "user_types" ("id", "key", "name", "description", "attributes", "used_for", "is_default") + VALUES (2,'posix','POSIX User','A POSIX user (with a home directory and shell access)','{"auto_form_fields":{"cn":{"data":["givenname","sn"]},"displayname":{"data":["givenname","sn"]},"gidnumber":[],"homedirectory":{"data":["givenname","sn"]},"uid":{"data":["givenname","sn"]},"uidnumber":[],"userpassword":{"optional":true}},"form_fields":{"givenname":[],"initials":{"optional":true},"preferredlanguage":{"type":"select","values":["en_US","de_DE","de_CH","en_GB","fi_FI","fr_FR","hu_HU"]},"loginshell":{"type":"select","values":["/bin/bash","/usr/bin/git-shell","/sbin/nologin"]},"ou":{"type":"select"},"sn":[],"title":{"optional":true},"userpassword":{"optional":true}},"fields":{"objectclass":["top","inetorgperson","organizationalperson","person","posixaccount"]}}',NULL,0) + INTO "user_types" ("id", "key", "name", "description", "attributes", "used_for", "is_default") + VALUES (3,'kolab_posix','Mail-enabled POSIX User','A mail-enabled POSIX User','{"auto_form_fields":{"alias":{"data":["givenname","preferredlanguage","sn"]},"cn":{"data":["givenname","preferredlanguage","sn"]},"displayname":{"data":["givenname","preferredlanguage","sn"]},"gidnumber":[],"homedirectory":{"data":["givenname","preferredlanguage","sn"]},"mail":{"data":["givenname","preferredlanguage","sn"]},"mailhost":{"optional":true},"uid":{"data":["givenname","preferredlanguage","sn"]},"uidnumber":[],"userpassword":{"optional":true}},"form_fields":{"alias":{"optional":true},"givenname":[],"initials":{"optional":true},"kolabdelegate":{"type":"list","autocomplete":true,"optional":true},"kolabinvitationpolicy":{"type":"select","values":["","ACT_MANUAL","ACT_REJECT"],"optional":true},"kolaballowsmtprecipient":{"type":"list","optional":true},"kolaballowsmtpsender":{"type":"list","optional":true},"l":{"optional":true},"loginshell":{"type":"select","values":["/bin/bash","/usr/bin/git-shell","/sbin/nologin"]},"mailalternateaddress":{"type":"list","optional":true},"mailquota":{"type":"text-quota","optional":true},"mobile":{"optional":true},"nsroledn":{"type":"list","autocomplete":true,"optional":true},"o":{"optional":true},"ou":{"type":"select"},"pager":{"optional":true},"postalcode":{"optional":true},"preferredlanguage":{"type":"select"},"sn":[],"street":{"optional":true},"telephonenumber":{"optional":true},"title":{"optional":true},"userpassword":{"optional":true}},"fields":{"objectclass":["top","inetorgperson","kolabinetorgperson","mailrecipient","organizationalperson","person","posixaccount"]}}',NULL,0) + INTO "user_types" ("id", "key", "name", "description", "attributes", "used_for", "is_default") + VALUES (4,'contact','Contact','A global address book contact','{"auto_form_fields":{"cn":{"data":["givenname","sn"]},"displayname":{"data":["givenname","sn"]},"uid":{"data":["givenname","sn"]},"userpassword":{"optional":true}},"form_fields":{"cn":{"optional":true},"displayname":{"optional":true},"givenname":[],"initials":{"optional":true},"l":{"optional":true},"mail":{"type":"list","optional":true},"mailalternateaddress":{"type":"list","optional":true},"mobile":{"optional":true},"o":{"optional":true},"ou":{"type":"select"},"pager":{"optional":true},"postalcode":{"optional":true},"sn":[],"street":{"optional":true},"telephonenumber":{"optional":true},"title":{"optional":true},"userpassword":{"optional":true}},"fields":{"objectclass":["top","inetorgperson","mailrecipient","organizationalperson","person"]}}',NULL,0) + INTO "user_types" ("id", "key", "name", "description", "attributes", "used_for", "is_default") + VALUES (5,'forwarding','Mail Forwarding','A mail forwarding account (forwarding only!)','{"auto_form_fields":{"cn":{"data":["givenname","sn"]},"displayname":{"data":["givenname","sn"]},"uid":{"data":["givenname","sn"]},"userpassword":{"optional":true}},"form_fields":{"cn":{"optional":true},"displayname":{"optional":true},"givenname":[],"initials":{"optional":true},"l":{"optional":true},"mail":{"type":"list","optional":true},"mailalternateaddress":{"type":"list","optional":true},"mailforwardingaddress":{"type":"list"},"mobile":{"optional":true},"o":{"optional":true},"ou":{"type":"select"},"pager":{"optional":true},"postalcode":{"optional":true},"sn":[],"street":{"optional":true},"telephonenumber":{"optional":true},"title":{"optional":true},"userpassword":{"optional":true}},"fields":{"objectclass":["top","inetorgperson","mailrecipient","organizationalperson","person"]}}',NULL,0) +SELECT 1 FROM DUAL; diff --git a/lib/SQL.php b/lib/SQL.php index d568200..add6fa6 100644 --- a/lib/SQL.php +++ b/lib/SQL.php @@ -42,6 +42,8 @@ class SQL 'identifier_end' => '"', ); + const DEFAULT_QUOTE = '`'; + /** * This implements the 'singleton' design pattern @@ -82,7 +84,8 @@ class SQL 'sybase' => 'mssql', 'dblib' => 'mssql', 'mysqli' => 'mysql', - 'oracle' => 'oci', + 'oci' => 'oracle', + 'oci8' => 'oracle', ); $driver = isset($driver_map[$driver]) ? $driver_map[$driver] : $driver; @@ -172,6 +175,9 @@ class SQL $query = $this->set_limit($query, $numrows, $offset); } + // replace self::DEFAULT_QUOTE with driver-specific quoting + $query = $this->query_parse($query); + $pos = 0; $idx = 0; @@ -212,6 +218,51 @@ class SQL } /** + * Parse SQL query and replace identifier quoting + * + * @param string $query SQL query + * + * @return string SQL query + */ + protected function query_parse($query) + { + $start = $this->options['identifier_start']; + $end = $this->options['identifier_end']; + $quote = self::DEFAULT_QUOTE; + + if ($start == $quote) { + return $query; + } + + $pos = 0; + $in = false; + + while ($pos = strpos($query, $quote, $pos)) { + if ($query[$pos+1] == $quote) { // skip escaped quote + $pos += 2; + } + else { + if ($in) { + $q = $end; + $in = false; + } + else { + $q = $start; + $in = true; + } + + $query = substr_replace($query, $q, $pos, 1); + $pos++; + } + } + + // replace escaped quote back to normal, see self::quote() + $query = str_replace($quote.$quote, $quote, $query); + + return $query; + } + + /** * Helper method to handle DB errors. * This by default logs the error but could be overriden by a driver implementation * @@ -259,10 +310,6 @@ class SQL */ public function fetch_assoc($result = null) { - if (!$this->connect()) { - return null; - } - return $this->fetch_row($result, PDO::FETCH_ASSOC); } @@ -347,7 +394,9 @@ class SQL $type = isset($map[$type]) ? $map[$type] : PDO::PARAM_STR; - return strtr($this->conn->quote($input, $type), array('?' => '??')); // escape ? + return strtr($this->conn->quote($input, $type), array( + '?' => '??', self::DEFAULT_QUOTE => self::DEFAULT_QUOTE.self::DEFAULT_QUOTE + )); } /** diff --git a/lib/SQL/oracle.php b/lib/SQL/oracle.php new file mode 100644 index 0000000..4bc072c --- /dev/null +++ b/lib/SQL/oracle.php @@ -0,0 +1,467 @@ +<?php + +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2014, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 of the License, or | + | (at your option) any later version. | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + +--------------------------------------------------------------------------+ +*/ + +/** + * Database independent query interface + */ +class SQL_oracle extends SQL +{ + public $db_provider = 'oracle'; + + /** + * Connects to database + */ + protected function connect() + { + if (!$this->conn && !$this->conn_tried) { + Log::debug("SQL: Connecting to " . $this->sql_uri); + + $this->conn_tried = true; + + $dsn = self::parse_dsn($this->sql_uri); + + // Get database specific connection options + $dsn_options = $this->dsn_options($dsn); + + $function = $this->db_pconn ? 'oci_pconnect' : 'oci_connect'; + + if (!function_exists($function)) { + $this->db_error = true; + $this->db_error_msg = 'OCI8 extension not loaded. See http://php.net/manual/en/book.oci8.php'; + + Log::error($this->db_error_msg); + return; + } + + // connect + $dbh = @$function($dsn['username'], $dsn['password'], $dsn_options['database'], $dsn_options['charset']); + + if (!$dbh) { + $error = oci_error(); + $this->db_error = true; + $this->db_error_msg = $error['message']; + + Log::error($this->db_error_msg); + return; + } + + // configure session + $this->conn_configure($dsn, $dbh); + + $this->conn = $dbh; + } + + return $this->conn; + } + + /** + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler + */ + protected function conn_configure($dsn, $dbh) + { + $init_queries = array( + "ALTER SESSION SET nls_date_format = 'YYYY-MM-DD'", + "ALTER SESSION SET nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'", + ); + + foreach ($init_queries as $query) { + $stmt = oci_parse($dbh, $query); + oci_execute($stmt); + } + } + + /** + * Execute a SQL query with limits + * + * @param string $query SQL query to execute + * @param array $params Values to be inserted in query + * @param int $offset Offset for LIMIT statement + * @param int $numrows Number of rows for LIMIT statement + * + * @return resource|bool Query handle or False on error + */ + public function query($query, $params = array(), $offset = null, $numrows = null) + { + if (!$this->connect()) { + return $this->last_result = false; + } + + $query = ltrim($query); + + if ($numrows || $offset) { + $query = $this->set_limit($query, $numrows, $offset); + } + + // replace self::DEFAULT_QUOTE with driver-specific quoting + $query = $this->query_parse($query); + + // Because in Roundcube we mostly use queries that are + // executed only once, we will not use prepared queries + $pos = 0; + $idx = 0; + $args = array(); + + if (count($params)) { + while ($pos = strpos($query, '?', $pos)) { + if ($query[$pos+1] == '?') { // skip escaped '?' + $pos += 2; + } + else { + $val = $this->quote($params[$idx++]); + + // long strings are not allowed inline, need to be parametrized + if (strlen($val) > 4000) { + $key = ':param' . (count($args) + 1); + $args[$key] = $params[$idx-1]; + $val = $key; + } + + unset($params[$idx-1]); + $query = substr_replace($query, $val, $pos, 1); + $pos += strlen($val); + } + } + } + + // replace escaped '?' back to normal, see self::quote() + $query = str_replace('??', '?', $query); + $query = rtrim($query, " \t\n\r\0\x0B;"); + + // log query + Log::debug('SQL: ' . $query); + + // destroy reference to previous result + $this->last_result = null; + $this->db_error_msg = null; + + // prepare query + $result = @oci_parse($this->conn, $query); + $mode = $this->in_transaction ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS; + + if ($result) { + foreach ($args as $param => $arg) { + oci_bind_by_name($result, $param, $args[$param], -1, SQLT_LNG); + } + } + + // execute query + if (!$result || !@oci_execute($result, $mode)) { + $result = $this->handle_error($query, $result); + } + + return $this->last_result = $result; + } + + /** + * Helper method to handle DB errors. + * This by default logs the error but could be overriden by a driver implementation + * + * @param string Query that triggered the error + * @return mixed Result to be stored and returned + */ + protected function handle_error($query, $result = null) + { + $error = oci_error(is_resource($result) ? $result : $this->conn); + + // @TODO: Find error codes for key errors + if (empty($this->options['ignore_key_errors']) || !in_array($error['code'], array('23000', '23505'))) { + $this->db_error = true; + $this->db_error_msg = sprintf('[%s] %s', $error['code'], $error['message']); + + Log::error($this->db_error_msg . " (SQL Query: $query)"); + } + + return false; + } + + /** + * Get last inserted record ID + * + * @param string $table Table name (to find the incremented sequence) + * + * @return mixed ID or false on failure + */ + public function insert_id($table = null) + { + $sequence = $this->quote_identifier($this->sequence_name($table)); + $result = $this->query("SELECT $sequence.currval FROM dual"); + $result = $this->fetch_array($result); + + return $result[0] ?: false; + } + + /** + * Get number of affected rows for the last query + * + * @param mixed $result Optional query handle + * + * @return int Number of (matching) rows + */ + public function affected_rows($result = null) + { + if ($result || ($result === null && ($result = $this->last_result))) { + return oci_num_rows($result); + } + + return 0; + } + + /** + * Get an associative array for one row + * If no query handle is specified, the last query will be taken as reference + * + * @param mixed $result Optional query handle + * + * @return mixed Array with col values or false on failure + */ + public function fetch_assoc($result = null) + { + return $this->_fetch_row($result, OCI_ASSOC); + } + + /** + * Get an index array for one row + * If no query handle is specified, the last query will be taken as reference + * + * @param mixed $result Optional query handle + * + * @return mixed Array with col values or false on failure + */ + public function fetch_array($result = null) + { + return $this->_fetch_row($result, OCI_NUM); + } + + /** + * Get col values for a result row + * + * @param mixed $result Optional query handle + * @param int $mode Fetch mode identifier + * + * @return mixed Array with col values or false on failure + */ + protected function _fetch_row($result, $mode) + { + if ($result || ($result === null && ($result = $this->last_result))) { + return oci_fetch_array($result, $mode + OCI_RETURN_NULLS + OCI_RETURN_LOBS); + } + + return false; + } + + /** + * Formats input so it can be safely used in a query + * PDO_OCI does not implement quote() method + * + * @param mixed $input Value to quote + * @param string $type Type of data (integer, bool, ident) + * + * @return string Quoted/converted string for use in query + */ + public function quote($input, $type = null) + { + // handle int directly for better performance + if ($type == 'integer' || $type == 'int') { + return intval($input); + } + + if (is_null($input)) { + return 'NULL'; + } + + if ($type == 'ident') { + return $this->quote_identifier($input); + } + + switch ($type) { + case 'bool': + case 'integer': + return intval($input); + default: + return "'" . strtr($input, array( + '?' => '??', + "'" => "''", + SQL::DEFAULT_QUOTE => SQL::DEFAULT_QUOTE . SQL::DEFAULT_QUOTE + )) . "'"; + } + } + + /** + * Return correct name for a specific database sequence + * + * @param string $table Table name + * + * @return string Translated sequence name + */ + protected function sequence_name($table) + { + // Note: we support only one sequence per table + // Note: The sequence name must be <table_name>_seq + return $table . '_seq'; + } + + /** + * Return SQL statement for case insensitive LIKE + * + * @param string $column Field name + * @param string $value Search value + * + * @return string SQL statement to use in query + */ + public function ilike($column, $value) + { + return 'UPPER(' . $this->quote_identifier($column) . ') LIKE UPPER(' . $this->quote($value) . ')'; + } + + /** + * Return SQL function for current time and date + * + * @param int $interval Optional interval (in seconds) to add/subtract + * + * @return string SQL function to use in query + */ + public function now($interval = 0) + { + if ($interval) { + $interval = intval($interval); + return "current_timestamp + INTERVAL '$interval' SECOND"; + } + + return "current_timestamp"; + } + + /** + * Adds TOP (LIMIT,OFFSET) clause to the query + * + * @param string $query SQL query + * @param int $limit Number of rows + * @param int $offset Offset + * + * @return string SQL query + */ + protected function set_limit($query, $limit = 0, $offset = 0) + { + $limit = intval($limit); + $offset = intval($offset); + $end = $offset + $limit; + + // @TODO: Oracle 12g has better OFFSET support + + if (!$offset) { + $query = "SELECT * FROM ($query) a WHERE rownum <= $end"; + } + else { + $query = "SELECT * FROM (SELECT a.*, rownum as rn FROM ($query) a WHERE rownum <= $end) b WHERE rn > $offset"; + } + + return $query; + } + + /** + * Returns connection options from DSN array + */ + protected function dsn_options($dsn) + { + $params = array(); + + if ($dsn['hostspec']) { + $host = $dsn['hostspec']; + if ($dsn['port']) { + $host .= ':' . $dsn['port']; + } + + $params['database'] = $host . '/' . $dsn['database']; + } + + $params['charset'] = 'UTF8'; + + return $params; + } + + /** + * Start transaction + * + * @return bool True on success, False on failure + */ + public function startTransaction() + { + if (!$this->connect()) { + return $this->last_result = false; + } + + Log::debug('BEGIN TRANSACTION'); + + return $this->last_result = $this->in_transaction = true; + } + + /** + * Commit transaction + * + * @return bool True on success, False on failure + */ + public function endTransaction() + { + if (!$this->connect()) { + return $this->last_result = false; + } + + Log::debug('COMMIT TRANSACTION'); + + if ($result = @oci_commit($this->conn)) { + $this->in_transaction = true; + } + else { + $this->handle_error('COMMIT'); + } + + return $this->last_result = $result; + } + + /** + * Rollback transaction + * + * @return bool True on success, False on failure + */ + public function rollbackTransaction() + { + if (!$this->connect()) { + return $this->last_result = false; + } + + Log::debug('ROLLBACK TRANSACTION'); + + if ($result = @oci_rollback($this->conn)) { + $this->in_transaction = false; + } + else { + $this->handle_error('ROLLBACK'); + } + + return $this->last_result = $this->conn->rollBack(); + } +} diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php index f1e0960..18867a4 100644 --- a/lib/api/kolab_api_service_form_value.php +++ b/lib/api/kolab_api_service_form_value.php @@ -1425,7 +1425,7 @@ class kolab_api_service_form_value extends kolab_api_service } $db = SQL::get_instance(); - $query = $db->query("SELECT option_values FROM options WHERE attribute = ?", array($attribute)); + $query = $db->query("SELECT `option_values` FROM `options` WHERE `attribute` = ?", array($attribute)); $result = $db->fetch_assoc($query); $result = json_decode($result['option_values']); diff --git a/lib/api/kolab_api_service_type.php b/lib/api/kolab_api_service_type.php index 868018b..09bbc8c 100644 --- a/lib/api/kolab_api_service_type.php +++ b/lib/api/kolab_api_service_type.php @@ -104,16 +104,16 @@ class kolab_api_service_type extends kolab_api_service $query = array_map(array($this->db, 'quote'), $query); $columns = array_map(array($this->db, 'quote_identifier'), array_keys($query)); - $this->db->query("INSERT INTO {$type}_types" + $this->db->query("INSERT INTO `{$type}_types`" . " (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $query) . ")"); - if (!($id = $this->db->insert_id())) { + if (!($id = $this->db->insert_id($type . '_types'))) { return false; } // there can be only one default if ($postdata['is_default']) { - $this->db->query("UPDATE {$type}_types SET is_default = 0 WHERE id <> " . intval($id)); + $this->db->query("UPDATE `{$type}_types` SET `is_default` = 0 WHERE `id` <> " . intval($id)); } // update cache @@ -150,7 +150,7 @@ class kolab_api_service_type extends kolab_api_service return false; } - $this->db->query("DELETE FROM {$object_name}_types WHERE id = " . intval($object_id)); + $this->db->query("DELETE FROM `{$object_name}_types` WHERE `id` = " . intval($object_id)); return (bool) $this->db->affected_rows(); } @@ -199,8 +199,8 @@ class kolab_api_service_type extends kolab_api_service $query[$idx] = $this->db->quote_identifier($idx) . " = " . $this->db->quote($value); } - $result = $this->db->query("UPDATE {$type}_types SET " - . implode(', ', $query) . " WHERE id = " . intval($postdata['id'])); + $result = $this->db->query("UPDATE `{$type}_types` SET " + . implode(', ', $query) . " WHERE `id` = " . intval($postdata['id'])); if (!$result) { return false; @@ -208,7 +208,7 @@ class kolab_api_service_type extends kolab_api_service // there can be only one default if ($postdata['is_default']) { - $this->db->query("UPDATE {$type}_types SET is_default = 0 WHERE id <> " . intval($postdata['id'])); + $this->db->query("UPDATE `{$type}_types` SET `is_default` = 0 WHERE `id` <> " . intval($postdata['id'])); } // update cache diff --git a/lib/kolab_api_service.php b/lib/kolab_api_service.php index f5c0804..9c55763 100644 --- a/lib/kolab_api_service.php +++ b/lib/kolab_api_service.php @@ -246,7 +246,7 @@ abstract class kolab_api_service ); } else { - $sql_result = $this->db->query("SELECT * FROM {$object_name}_types ORDER BY name"); + $sql_result = $this->db->query("SELECT * FROM `{$object_name}_types` ORDER BY `name`"); $object_types = array(); while ($row = $this->db->fetch_assoc($sql_result)) { |