Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
orbea -
OpenLDAP
Commits
efc8c43b
Commit
efc8c43b
authored
Feb 20, 2005
by
Howard Chu
Browse files
Adding back-config pieces
parent
a26148af
Changes
5
Hide whitespace changes
Inline
Side-by-side
servers/slapd/Makefile.in
View file @
efc8c43b
...
...
@@ -23,7 +23,7 @@ SUBDIRS=back-* shell-backends slapi overlays
NT_SRCS
=
nt_svc.c
NT_OBJS
=
nt_svc.o ../../libraries/liblutil/slapdmsg.res
SRCS
=
main.c globals.c config.c daemon.c
\
SRCS
=
main.c globals.c
bconfig.c
config.c daemon.c
\
connection.c search.c filter.c add.c cr.c
\
attr.c entry.c backend.c result.c operation.c
\
dn.c compare.c modify.c delete.c modrdn.c ch_malloc.c
\
...
...
@@ -40,7 +40,7 @@ SRCS = main.c globals.c config.c daemon.c \
slappasswd.c slaptest.c slapauth.c slapacl.c component.c
\
$
(
@PLAT@_SRCS
)
OBJS
=
main.o globals.o config.o daemon.o
\
OBJS
=
main.o globals.o
bconfig.o
config.o daemon.o
\
connection.o search.o filter.o add.o cr.o
\
attr.o entry.o backend.o result.o operation.o
\
dn.o compare.o modify.o delete.o modrdn.o ch_malloc.o
\
...
...
servers/slapd/bconfig.c
0 → 100644
View file @
efc8c43b
/* bconfig.c - the config backend */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2005 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/* ACKNOWLEDGEMENTS:
* This work was originally developed by Howard Chu for inclusion
* in OpenLDAP Software.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include "slap.h"
#include "config.h"
#define CONFIG_DN "cn=config"
typedef
struct
CfEntryInfo
{
struct
CfEntryInfo
*
ce_sibs
;
struct
CfEntryInfo
*
ce_kids
;
Entry
*
ce_entry
;
ConfigTable
*
ce_table
;
}
CfEntryInfo
;
typedef
struct
{
ConfigFile
*
cb_config
;
CfEntryInfo
*
cb_root
;
}
CfBackInfo
;
static
int
config_back_bind
(
Operation
*
op
,
SlapReply
*
rs
)
{
if
(
op
->
orb_method
==
LDAP_AUTH_SIMPLE
&&
be_isroot_pw
(
op
))
{
ber_dupbv
(
&
op
->
orb_edn
,
be_root_dn
(
op
->
o_bd
));
/* frontend sends result */
return
LDAP_SUCCESS
;
}
rs
->
sr_err
=
LDAP_INVALID_CREDENTIALS
;
send_ldap_result
(
op
,
rs
);
return
rs
->
sr_err
;
}
static
CfEntryInfo
*
config_find_base
(
CfEntryInfo
*
root
,
struct
berval
*
dn
,
CfEntryInfo
**
last
)
{
struct
berval
cdn
;
char
*
c
;
if
(
dn_match
(
&
root
->
ce_entry
->
e_nname
,
dn
))
return
root
;
c
=
dn
->
bv_val
+
dn
->
bv_len
;
for
(;
*
c
!=
','
;
c
--
);
while
(
root
)
{
*
last
=
root
;
for
(
--
c
;
c
>
dn
->
bv_val
&&
*
c
!=
','
;
c
--
);
if
(
*
c
==
','
)
c
++
;
cdn
.
bv_val
=
c
;
cdn
.
bv_len
=
dn
->
bv_len
-
(
c
-
dn
->
bv_val
);
root
=
root
->
ce_kids
;
for
(;
root
;
root
=
root
->
ce_sibs
)
{
if
(
dn_match
(
&
root
->
ce_entry
->
e_nname
,
&
cdn
))
{
if
(
cdn
.
bv_val
==
dn
->
bv_val
)
{
return
root
;
}
break
;
}
}
}
return
root
;
}
static
int
config_send
(
Operation
*
op
,
SlapReply
*
rs
,
CfEntryInfo
*
ce
,
int
depth
)
{
int
rc
=
0
;
if
(
test_filter
(
op
,
ce
->
ce_entry
,
op
->
ors_filter
)
==
LDAP_COMPARE_TRUE
)
{
rs
->
sr_attrs
=
op
->
ors_attrs
;
rs
->
sr_entry
=
ce
->
ce_entry
;
rc
=
send_search_entry
(
op
,
rs
);
}
if
(
op
->
ors_scope
==
LDAP_SCOPE_SUBTREE
)
{
if
(
ce
->
ce_kids
)
{
rc
=
config_send
(
op
,
rs
,
ce
->
ce_kids
,
1
);
if
(
rc
)
return
rc
;
}
if
(
depth
)
{
for
(
ce
=
ce
->
ce_sibs
;
ce
;
ce
=
ce
->
ce_sibs
)
{
rc
=
config_send
(
op
,
rs
,
ce
,
0
);
if
(
rc
)
break
;
}
}
}
return
rc
;
}
static
int
config_back_search
(
Operation
*
op
,
SlapReply
*
rs
)
{
CfBackInfo
*
cfb
;
CfEntryInfo
*
ce
,
*
last
;
int
rc
;
if
(
!
be_isroot
(
op
)
)
{
rs
->
sr_err
=
LDAP_INSUFFICIENT_ACCESS
;
send_ldap_result
(
op
,
rs
);
}
cfb
=
(
CfBackInfo
*
)
op
->
o_bd
->
be_private
;
ce
=
config_find_base
(
cfb
->
cb_root
,
&
op
->
o_req_ndn
,
&
last
);
if
(
!
ce
)
{
if
(
last
)
rs
->
sr_matched
=
last
->
ce_entry
->
e_name
.
bv_val
;
rs
->
sr_err
=
LDAP_NO_SUCH_OBJECT
;
goto
out
;
}
switch
(
op
->
ors_scope
)
{
case
LDAP_SCOPE_BASE
:
case
LDAP_SCOPE_SUBTREE
:
config_send
(
op
,
rs
,
ce
,
0
);
break
;
case
LDAP_SCOPE_ONELEVEL
:
for
(
ce
=
ce
->
ce_kids
;
ce
;
ce
=
ce
->
ce_sibs
)
{
config_send
(
op
,
rs
,
ce
,
1
);
}
break
;
}
rs
->
sr_err
=
LDAP_SUCCESS
;
out:
send_ldap_result
(
op
,
rs
);
return
0
;
}
static
Entry
*
config_alloc_entry
(
struct
berval
*
pdn
,
struct
berval
*
rdn
)
{
Entry
*
e
=
ch_calloc
(
1
,
sizeof
(
Entry
)
);
CfEntryInfo
*
ce
=
ch_calloc
(
1
,
sizeof
(
CfEntryInfo
)
);
e
->
e_private
=
ce
;
ce
->
ce_entry
=
e
;
build_new_dn
(
&
e
->
e_name
,
pdn
,
rdn
,
NULL
);
ber_dupbv
(
&
e
->
e_nname
,
&
e
->
e_name
);
return
e
;
}
static
int
config_build_entry
(
Entry
*
e
,
void
*
private
,
char
*
oc
,
struct
berval
*
rdn
)
{
struct
berval
vals
[
2
];
struct
berval
ad_name
;
AttributeDescription
*
ad
=
NULL
;
int
rc
;
char
*
ptr
;
const
char
*
text
;
BER_BVZERO
(
&
vals
[
1
]
);
ber_str2bv
(
oc
,
0
,
0
,
&
vals
[
0
]
);
attr_merge
(
e
,
slap_schema
.
si_ad_objectClass
,
vals
,
NULL
);
ptr
=
strchr
(
rdn
->
bv_val
,
'='
);
ad_name
.
bv_val
=
rdn
->
bv_val
;
ad_name
.
bv_len
=
ptr
-
rdn
->
bv_val
;
rc
=
slap_bv2ad
(
&
ad_name
,
&
ad
,
&
text
);
if
(
rc
)
{
return
rc
;
}
vals
[
0
].
bv_val
=
ptr
+
1
;
vals
[
0
].
bv_len
=
rdn
->
bv_len
-
(
vals
[
0
].
bv_val
-
rdn
->
bv_val
);
attr_merge
(
e
,
ad
,
vals
,
NULL
);
return
0
;
}
static
int
config_back_db_open
(
BackendDB
*
be
)
{
CfBackInfo
*
cfb
=
be
->
be_private
;
struct
berval
rdn
;
Entry
*
e
,
*
parent
;
CfEntryInfo
*
ce
,
*
ceparent
,
*
ceprev
;
int
i
,
buflen
=
0
;
char
*
buf
=
NULL
;
BackendInfo
*
bi
;
BackendDB
*
bptr
;
/* create root of tree */
ber_str2bv
(
CONFIG_DN
,
STRLENOF
(
CONFIG_DN
),
0
,
&
rdn
);
e
=
config_alloc_entry
(
NULL
,
&
rdn
);
ce
=
e
->
e_private
;
ce
->
ce_table
=
be
->
bd_info
->
bi_cf_table
;
cfb
->
cb_root
=
ce
;
config_build_entry
(
e
,
be
->
be_private
,
"olcGlobal"
,
&
rdn
);
parent
=
e
;
ceparent
=
ce
;
/* Create backend nodes. Skip if they don't provide a cf_table.
* There usually aren't any of these.
*/
bi
=
backendInfo
;
for
(
i
=
0
;
i
<
nBackendInfo
;
i
++
,
bi
++
)
{
if
(
!
bi
->
bi_cf_table
)
continue
;
if
(
!
bi
->
bi_private
)
continue
;
if
(
buflen
<
STRLENOF
(
"olcbackend="
)
+
strlen
(
bi
->
bi_type
)
+
1
)
{
buflen
=
STRLENOF
(
"olcbackend="
)
+
strlen
(
bi
->
bi_type
)
+
1
;
buf
=
realloc
(
buf
,
buflen
);
}
rdn
.
bv_val
=
buf
;
rdn
.
bv_len
=
sprintf
(
buf
,
"olcBackend=%s"
,
bi
->
bi_type
);
e
=
config_alloc_entry
(
&
parent
->
e_nname
,
&
rdn
);
ce
=
e
->
e_private
;
ce
->
ce_table
=
bi
->
bi_cf_table
;
config_build_entry
(
e
,
bi
->
bi_private
,
"olcBackendConfig"
,
&
rdn
);
if
(
!
ceparent
->
ce_kids
)
{
ceparent
->
ce_kids
=
ce
;
}
else
{
ceprev
->
ce_sibs
=
ce
;
}
ceprev
=
ce
;
}
/* Create database nodes... */
for
(
i
=
0
;
i
<
nBackendDB
;
i
++
)
{
if
(
i
==
0
)
{
bptr
=
frontendDB
;
}
else
{
bptr
=
&
backendDB
[
i
];
}
bi
=
bptr
->
bd_info
;
if
(
buflen
<
STRLENOF
(
"olcdatabase={xxxxxxxx}"
)
+
strlen
(
bi
->
bi_type
)
+
1
)
{
buflen
=
STRLENOF
(
"olcdatabase={xxxxxxxx}"
)
+
strlen
(
bi
->
bi_type
)
+
1
;
buf
=
realloc
(
buf
,
buflen
);
}
rdn
.
bv_val
=
buf
;
rdn
.
bv_len
=
sprintf
(
buf
,
"olcDatabase={%0x}%s"
,
i
,
bi
->
bi_type
);
e
=
config_alloc_entry
(
&
parent
->
e_nname
,
&
rdn
);
ce
=
e
->
e_private
;
ce
->
ce_table
=
bptr
->
be_cf_table
;
config_build_entry
(
e
,
bptr
->
be_private
,
"olcDatabaseConfig"
,
&
rdn
);
if
(
!
ceparent
->
ce_kids
)
{
ceparent
->
ce_kids
=
ce
;
}
else
{
ceprev
->
ce_sibs
=
ce
;
}
ceprev
=
ce
;
/* Iterate through overlays */
}
/* Create includeFile nodes... */
return
0
;
}
static
int
config_back_db_destroy
(
Backend
*
be
)
{
free
(
be
->
be_private
);
return
0
;
}
int
config_back_initialize
(
BackendInfo
*
bi
)
{
bi
->
bi_open
=
0
;
bi
->
bi_close
=
0
;
bi
->
bi_config
=
0
;
bi
->
bi_destroy
=
0
;
bi
->
bi_db_init
=
0
;
bi
->
bi_db_config
=
0
;
bi
->
bi_db_open
=
config_back_db_open
;
bi
->
bi_db_close
=
0
;
bi
->
bi_db_destroy
=
config_back_db_destroy
;
bi
->
bi_op_bind
=
config_back_bind
;
bi
->
bi_op_unbind
=
0
;
bi
->
bi_op_search
=
config_back_search
;
bi
->
bi_op_compare
=
0
;
bi
->
bi_op_modify
=
0
;
bi
->
bi_op_modrdn
=
0
;
bi
->
bi_op_add
=
0
;
bi
->
bi_op_delete
=
0
;
bi
->
bi_op_abandon
=
0
;
bi
->
bi_extended
=
0
;
bi
->
bi_chk_referrals
=
0
;
bi
->
bi_connection_init
=
0
;
bi
->
bi_connection_destroy
=
0
;
return
0
;
}
void
config_back_init
(
ConfigFile
*
cfp
,
ConfigTable
*
ct
)
{
BackendInfo
bi
=
{
0
};
BackendDB
*
be
;
struct
berval
dn
;
CfBackInfo
*
cfb
;
bi
.
bi_type
=
"config"
;
bi
.
bi_init
=
config_back_initialize
;
bi
.
bi_cf_table
=
ct
;
backend_add
(
&
bi
);
be
=
backend_db_init
(
bi
.
bi_type
);
ber_str2bv
(
CONFIG_DN
,
0
,
1
,
&
be
->
be_rootdn
);
ber_dupbv
(
&
be
->
be_rootndn
,
&
be
->
be_rootdn
);
ber_dupbv
(
&
dn
,
&
be
->
be_rootdn
);
ber_bvarray_add
(
&
be
->
be_suffix
,
&
dn
);
ber_dupbv
(
&
dn
,
&
be
->
be_rootdn
);
ber_bvarray_add
(
&
be
->
be_nsuffix
,
&
dn
);
cfb
=
ch_calloc
(
1
,
sizeof
(
CfBackInfo
));
cfb
->
cb_config
=
cfp
;
be
->
be_private
=
cfb
;
}
servers/slapd/config.c
View file @
efc8c43b
...
...
@@ -89,6 +89,9 @@ static char *strtok_quote(char *line, char *sep);
static int load_ucdata(char *path);
#endif
/* state info for back-config */
static
ConfigFile
cf_prv
,
*
cfn
=
&
cf_prv
;
int
read_config_file
(
const
char
*
fname
,
int
depth
,
ConfigArgs
*
cf
);
static
int
add_syncrepl
LDAP_P
((
Backend
*
,
char
**
,
int
));
...
...
@@ -297,7 +300,7 @@ ConfigTable SystemConfiguration[] = {
&
config_generic
,
"( OLcfgAt:29 NAME 'olcMaxDerefDepth' "
"SYNTAX OMsInteger )"
,
NULL
,
NULL
},
#ifdef SLAPD_MODULES
{
"moduleload"
,
"file"
,
2
,
2
,
0
,
ARG_MAGIC
|
CFG_MODLOAD
,
{
"moduleload"
,
"file"
,
2
,
0
,
0
,
ARG_MAGIC
|
CFG_MODLOAD
,
&
config_generic
,
"( OLcfgAt:30 NAME 'olcModuleLoad' "
"SYNTAX OMsDirectoryString )"
,
NULL
,
NULL
},
{
"modulepath"
,
"path"
,
2
,
2
,
0
,
ARG_MAGIC
|
CFG_MODPATH
,
...
...
@@ -620,7 +623,7 @@ int parse_config_table(ConfigTable *Conf, ConfigArgs *c) {
}
int
init_config_
schema
(
ConfigTable
*
ct
)
{
init_config_
attrs
(
ConfigTable
*
ct
)
{
LDAPAttributeType
*
at
;
int
i
,
code
;
const
char
*
err
;
...
...
@@ -654,13 +657,17 @@ read_config(const char *fname, int depth) {
int
i
;
char
*
argv
[
3
];
/* Schema initialization should normally be part of bi_open */
for
(
i
=
0
;
OidMacros
[
i
].
name
;
i
++
)
{
argv
[
1
]
=
OidMacros
[
i
].
name
;
argv
[
2
]
=
OidMacros
[
i
].
oid
;
parse_oidm
(
"slapd"
,
i
,
3
,
argv
);
}
i
=
init_config_
schema
(
SystemConfiguration
);
i
=
init_config_
attrs
(
SystemConfiguration
);
if
(
i
)
return
i
;
config_back_init
(
&
cf_prv
,
SystemConfiguration
);
return
read_config_file
(
fname
,
depth
,
NULL
);
}
...
...
@@ -696,6 +703,11 @@ read_config_file(const char *fname, int depth, ConfigArgs *cf)
fname
,
strerror
(
errno
),
errno
);
return
(
1
);
}
#ifdef SLAPD_MODULES
cfn
->
c_modlast
=
&
cfn
->
c_modpaths
;
#endif
ber_str2bv
(
fname
,
0
,
1
,
&
cfn
->
c_file
);
fname
=
cfn
->
c_file
.
bv_val
;
Debug
(
LDAP_DEBUG_CONFIG
,
"reading config file %s
\n
"
,
fname
,
0
,
0
);
...
...
@@ -827,7 +839,9 @@ config_generic(ConfigArgs *c) {
case
CFG_DATABASE
:
c
->
bi
=
NULL
;
if
(
!
(
c
->
be
=
backend_db_init
(
c
->
argv
[
1
])))
{
if
(
!
strcasecmp
(
c
->
argv
[
1
],
"config"
))
{
c
->
be
=
backendDB
;
}
else
if
(
!
(
c
->
be
=
backend_db_init
(
c
->
argv
[
1
])))
{
Debug
(
LDAP_DEBUG_ANY
,
"%s: "
"database %s failed init!
\n
"
,
c
->
log
,
c
->
argv
[
1
],
0
);
return
(
1
);
...
...
@@ -923,12 +937,6 @@ config_generic(ConfigArgs *c) {
parse_acl
(
c
->
be
,
c
->
fname
,
c
->
lineno
,
c
->
argc
,
c
->
argv
);
break
;
#if 0
case CFG_AUDITLOG:
c->be->be_auditlogfile = c->value_string;
break;
#endif
case
CFG_REPLOG
:
if
(
SLAP_MONITOR
(
c
->
be
))
{
Debug
(
LDAP_DEBUG_ANY
,
"%s: "
...
...
@@ -948,6 +956,11 @@ config_generic(ConfigArgs *c) {
c
->
log
,
0
,
0
);
return
(
1
);
}
{
struct
berval
bv
;
ber_str2bv
(
c
->
argv
[
1
],
0
,
1
,
&
bv
);
ber_bvarray_add
(
&
cfn
->
c_dseFiles
,
&
bv
);
}
break
;
case
CFG_LOGFILE
:
{
...
...
@@ -973,10 +986,31 @@ config_generic(ConfigArgs *c) {
case
CFG_MODLOAD
:
if
(
module_load
(
c
->
argv
[
1
],
c
->
argc
-
2
,
(
c
->
argc
>
2
)
?
c
->
argv
+
2
:
NULL
))
return
(
1
);
/* Record this load on the current path */
{
struct
berval
bv
;
ber_str2bv
(
c
->
line
,
0
,
1
,
&
bv
);
ber_bvarray_add
(
&
cfn
->
c_modlast
->
mp_loads
,
&
bv
);
}
break
;
case
CFG_MODPATH
:
if
(
module_path
(
c
->
argv
[
1
]))
return
(
1
);
/* Record which path was used with each module */
{
ModPaths
*
mp
;
if
(
!
cfn
->
c_modpaths
.
mp_loads
)
mp
=
&
cfn
->
c_modpaths
;
else
mp
=
ch_malloc
(
sizeof
(
ModPaths
));
ber_str2bv
(
c
->
argv
[
1
],
0
,
1
,
&
mp
->
mp_path
);
mp
->
mp_next
=
NULL
;
mp
->
mp_loads
=
NULL
;
cfn
->
c_modlast
->
mp_next
=
mp
;
cfn
->
c_modlast
=
mp
;
}
break
;
#endif
...
...
@@ -1635,12 +1669,26 @@ config_updateref(ConfigArgs *c) {
int
config_include
(
ConfigArgs
*
c
)
{
char
*
savefname
=
ch_strdup
(
c
->
argv
[
1
]);
unsigned
long
savelineno
=
c
->
lineno
;
int
rc
;
rc
=
read_config_file
(
savefname
,
c
->
depth
+
1
,
c
);
free
(
savefname
);
ConfigFile
*
cf
=
ch_calloc
(
1
,
sizeof
(
ConfigFile
));
ConfigFile
*
cfsave
=
cfn
;
ConfigFile
*
cf2
=
NULL
;
if
(
cfn
->
c_kids
)
{
for
(
cf2
=
cfn
->
c_kids
;
cf2
&&
cf2
->
c_sibs
;
cf2
=
cf2
->
c_sibs
)
;
cf2
->
c_sibs
=
cf
;
}
else
{
cfn
->
c_kids
=
cf
;
}
cfn
=
cf
;
rc
=
read_config_file
(
c
->
argv
[
1
],
c
->
depth
+
1
,
c
);
c
->
lineno
=
savelineno
-
1
;
cfn
=
cfsave
;
if
(
rc
)
{
if
(
cf2
)
cf2
->
c_sibs
=
NULL
;
else
cfn
->
c_kids
=
NULL
;
ch_free
(
cf
);
}
return
(
rc
);
}
...
...
servers/slapd/config.h
View file @
efc8c43b
...
...
@@ -15,7 +15,7 @@
* <http://www.OpenLDAP.org/license.html>.
*/
typedef
struct
c
onfig
_t
able
_s
{
typedef
struct
C
onfig
T
able
{
char
*
name
;
char
*
what
;
int
min_args
;
...
...
@@ -76,3 +76,24 @@ typedef struct config_args_s {
}
ConfigArgs
;
typedef
int
(
ConfigDriver
)(
ConfigArgs
*
c
);
#ifdef SLAPD_MODULES
typedef
struct
modpath_s
{
struct
modpath_s
*
mp_next
;
struct
berval
mp_path
;
BerVarray
mp_loads
;
}
ModPaths
;
#endif
typedef
struct
ConfigFile
{
struct
ConfigFile
*
c_sibs
;
struct
ConfigFile
*
c_kids
;
struct
berval
c_file
;
#ifdef SLAPD_MODULES
ModPaths
c_modpaths
;
ModPaths
*
c_modlast
;
#endif
BerVarray
c_dseFiles
;
}
ConfigFile
;
void
config_back_init
(
ConfigFile
*
cfp
,
ConfigTable
*
ct
);
servers/slapd/slap.h
View file @
efc8c43b
...
...
@@ -1704,6 +1704,7 @@ struct slap_backend_db {
char
*
be_realm
;
void
*
be_pb
;
/* Netscape plugin */
struct
ConfigTable
*
be_cf_table
;
void
*
be_private
;
/* anything the backend database needs */
};
...
...
@@ -1911,6 +1912,8 @@ typedef int (BI_tool_id2entry_get) LDAP_P(( BackendDB *be, ID id, Entry **e ));
typedef
ID
(
BI_tool_entry_modify
)
LDAP_P
((
BackendDB
*
be
,
Entry
*
e
,
struct
berval
*
text
));
struct
ConfigTable
;
/* config.h */
struct
slap_backend_info
{
char
*
bi_type
;
/* type of backend */
...
...
@@ -2033,6 +2036,7 @@ struct slap_backend_info {
char
bi_ctrls
[
SLAP_MAX_CIDS
+
1
];
unsigned
int
bi_nDB
;
/* number of databases of this type */
struct
ConfigTable
*
bi_cf_table
;
void
*
bi_private
;
/* anything the backend type needs */
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment