At line 3 added 19 lines |
|
This plugin expects a user.XML file response, a VFS.XML file response, and a custom VFS items response...example provided below: |
|
{{{ |
<?xml version="1.0" encoding="UTF-8"?> |
<VFS type="vector"> |
<VFS_subitem type="properties"> |
<type>DIR</type> |
<dir>/</dir> |
<name>HOME</name> |
<data type="vector"> |
<data_subitem type="properties"> |
<url>FILE://home/crushftp/testuser1/HOME/</url> |
<type>DIR</type> |
</data_subitem> |
</data> |
</VFS_subitem> |
</VFS>}}} |
|
At line 34 added 3 lines |
import crushftp.handlers.SessionCrush; |
import crushftp.handlers.UserTools; |
import crushftp.server.ServerStatus; |
At line 20 removed one line |
GUI g = null; |
At line 26 changed 15 lines |
p.put("enabled","false"); |
p.put("debug","false"); |
p.put("user_url","http://127.0.0.1/auth/user_properties.xml"); |
p.put("user_post_data",""); |
p.put("permissions_url","http://127.0.0.1/auth/vfs_properties.xml"); |
p.put("permissions_post_data",""); |
p.put("vfs_url","http://127.0.0.1/auth/vfs_vectors.xml"); |
p.put("vfs_post_data",""); |
p.put("method","GET"); |
p.put("username","%username%"); |
p.put("password",c.encode_pass("%password%","DES")); |
p.put("usernameTemplate",""); |
p.put("overwrite_vfs","true"); |
p.put("server_item","All"); |
p.put("version",version); |
p.put("enabled", "false"); |
p.put("debug", "false"); |
p.put("user_url", "http://127.0.0.1/auth/user_properties.xml"); |
p.put("user_post_data", ""); |
p.put("permissions_url", "http://127.0.0.1/auth/vfs_properties.xml"); |
p.put("permissions_post_data", ""); |
p.put("vfs_url", "http://127.0.0.1/auth/vfs_vectors.xml"); |
p.put("vfs_post_data", ""); |
p.put("method", "GET"); |
p.put("username", "%username%"); |
p.put("password", c.encode_pass("%password%", "DES")); |
p.put("usernameTemplate", ""); |
p.put("overwrite_vfs", "true"); |
p.put("server_item", "All"); |
p.put("version", version); |
At line 47 removed one line |
if (g != null) g.setSettings(settings); |
At line 52 changed 2 lines |
if (g != null) settings = g.getSettings(); |
settings.put("version",version); |
settings.put("version", version); |
At line 57 removed 8 lines |
public javax.swing.JPanel getPrefsPanel() |
{ |
if (g == null) g = new GUI(); |
g.setParent(this); |
g.setSettings(settings); |
return g; |
} |
|
At line 68 changed one line |
if (info.getProperty("action","").equals("login")) |
if (info.getProperty("action", "").equals("login")) |
At line 70 changed one line |
Properties user_info = (Properties)info.get("user_info"); |
Properties user_info = (Properties) info.get("user_info"); |
At line 72 changed one line |
if (info.getProperty("action","").equals("login") && (get("server_item").trim().equalsIgnoreCase("All") || get("server_item").trim().length() == 0 || user_info.getProperty("listen_ip_port","").equals(get("server_item").trim()))) |
if (info.getProperty("action", "").equals("login") && (get("server_item").trim().equalsIgnoreCase("All") || get("server_item").trim().length() == 0 || user_info.getProperty("listen_ip_port", "").equals(get("server_item").trim()))) |
At line 82 changed 5 lines |
try{the_line = Common.replace_str(the_line, "%user_name%", username);}catch(Exception e){} |
try{the_line = Common.replace_str(the_line, "%username%", username);}catch(Exception e){} |
try{the_line = Common.replace_str(the_line, "%user_password%", password);}catch(Exception e){} |
try{the_line = Common.replace_str(the_line, "%user_pass%", password);}catch(Exception e){} |
try{the_line = Common.replace_str(the_line, "%password%", password);}catch(Exception e){} |
try |
{ |
the_line = Common.replace_str(the_line, "%user_name%", username); |
} |
catch (Exception e) |
{ |
} |
try |
{ |
the_line = Common.replace_str(the_line, "%username%", username); |
} |
catch (Exception e) |
{ |
} |
try |
{ |
the_line = Common.replace_str(the_line, "%user_password%", password); |
} |
catch (Exception e) |
{ |
} |
try |
{ |
the_line = Common.replace_str(the_line, "%user_pass%", password); |
} |
catch (Exception e) |
{ |
} |
try |
{ |
the_line = Common.replace_str(the_line, "%password%", password); |
} |
catch (Exception e) |
{ |
} |
At line 90 changed one line |
|
|
At line 95 changed 3 lines |
Properties user = (Properties)info.get("user"); |
Properties user_info = (Properties)info.get("user_info"); |
crushftp.server.ServerSession thisSession = (crushftp.server.ServerSession)info.get("ServerSession"); |
Properties user = (Properties) info.get("user"); |
Properties user_info = (Properties) info.get("user_info"); |
SessionCrush thisSession = (SessionCrush) info.get("ServerSession"); |
At line 100 changed 2 lines |
username = username.replaceAll("/",""); |
username = username.replaceAll("\\\\",""); |
username = username.replaceAll("/", ""); |
username = username.replaceAll("\\\\", ""); |
At line 108 changed 6 lines |
String url = replace_vars(get("user_url"),username,password); |
String post_data = replace_vars(get("user_post_data"),username,password); |
try{url = thisSession.server_status_frame.change_vars_to_values(url,thisSession);}catch(Exception e){} |
try{post_data = thisSession.server_status_frame.change_vars_to_values(post_data,thisSession);}catch(Exception e){} |
String userXML = getResult(url, post_data,replace_vars(get("username"),username,password),replace_vars(c.decode_pass(get("password")),username,password)); |
Properties theUser = (Properties)Common.readXMLObject(new ByteArrayInputStream(userXML.getBytes("UTF8"))); |
String url = replace_vars(get("user_url"), username, password); |
String post_data = replace_vars(get("user_post_data"), username, password); |
try |
{ |
url = ServerStatus.thisObj.change_vars_to_values(url, thisSession); |
} |
catch (Exception e) |
{ |
} |
try |
{ |
post_data = ServerStatus.thisObj.change_vars_to_values(post_data, thisSession); |
} |
catch (Exception e) |
{ |
} |
String userXML = getResult(url, post_data, replace_vars(get("username"), username, password), replace_vars(c.decode_pass(get("password")), username, password)); |
Properties theUser = (Properties) Common.readXMLObject(new ByteArrayInputStream(userXML.getBytes("UTF8"))); |
At line 118 changed 6 lines |
url = replace_vars(get("permissions_url"),username,password); |
post_data = replace_vars(get("permissions_post_data"),username,password); |
try{url = thisSession.server_status_frame.change_vars_to_values(url,thisSession);}catch(Exception e){} |
try{post_data = thisSession.server_status_frame.change_vars_to_values(post_data,thisSession);}catch(Exception e){} |
String permissionsXML = getResult(url, post_data,replace_vars(get("username"),username,password),replace_vars(c.decode_pass(get("password")),username,password)); |
Properties permissions = (Properties)Common.readXMLObject(new ByteArrayInputStream(permissionsXML.getBytes("UTF8"))); |
url = replace_vars(get("permissions_url"), username, password); |
post_data = replace_vars(get("permissions_post_data"), username, password); |
try |
{ |
url = ServerStatus.thisObj.change_vars_to_values(url, thisSession); |
} |
catch (Exception e) |
{ |
} |
try |
{ |
post_data = ServerStatus.thisObj.change_vars_to_values(post_data, thisSession); |
} |
catch (Exception e) |
{ |
} |
String permissionsXML = getResult(url, post_data, replace_vars(get("username"), username, password), replace_vars(c.decode_pass(get("password")), username, password)); |
Properties permissions = (Properties) Common.readXMLObject(new ByteArrayInputStream(permissionsXML.getBytes("UTF8"))); |
At line 126 changed 6 lines |
url = replace_vars(get("vfs_url"),username,password); |
post_data = replace_vars(get("vfs_post_data"),username,password); |
try{url = thisSession.server_status_frame.change_vars_to_values(url,thisSession);}catch(Exception e){} |
try{post_data = thisSession.server_status_frame.change_vars_to_values(post_data,thisSession);}catch(Exception e){} |
String vfsXML = getResult(url, post_data,replace_vars(get("username"),username,password),replace_vars(c.decode_pass(get("password")),username,password)); |
Vector VFSItems = (Vector)Common.readXMLObject(new ByteArrayInputStream(vfsXML.getBytes("UTF8"))); |
url = replace_vars(get("vfs_url"), username, password); |
post_data = replace_vars(get("vfs_post_data"), username, password); |
try |
{ |
url = ServerStatus.thisObj.change_vars_to_values(url, thisSession); |
} |
catch (Exception e) |
{ |
} |
try |
{ |
post_data = ServerStatus.thisObj.change_vars_to_values(post_data, thisSession); |
} |
catch (Exception e) |
{ |
} |
String vfsXML = getResult(url, post_data, replace_vars(get("username"), username, password), replace_vars(c.decode_pass(get("password")), username, password)); |
Vector VFSItems = (Vector) Common.readXMLObject(new ByteArrayInputStream(vfsXML.getBytes("UTF8"))); |
At line 134 changed one line |
msg("got users information:"+theUser); |
msg("got users information:" + theUser); |
At line 137 changed one line |
boolean loadedUser = loadTemplateUser(user,info); |
boolean loadedUser = loadTemplateUser(user, info); |
At line 141 changed one line |
String users_path = System.getProperty("crushftp.plugins")+"plugins/WebApplication_VFS/"+username; |
String users_path = System.getProperty("crushftp.plugins") + "plugins/WebApplication_VFS/" + username; |
At line 144 changed 6 lines |
info.put("VFSItems",VFSItems); |
info.put("permissions",permissions); |
info.put("uVFS",users_path); |
info.put("templateUser",get("usernameTemplate")); |
info.put("overwrite_vfs",get("overwrite_vfs")); |
user.put("root_dir","/"); |
info.put("VFSItems", VFSItems); |
info.put("permissions", permissions); |
info.put("uVFS", users_path); |
info.put("templateUser", get("usernameTemplate")); |
info.put("overwrite_vfs", get("overwrite_vfs")); |
user.put("root_dir", "/"); |
At line 155 changed one line |
catch(Exception e) |
catch (Exception e) |
At line 160 changed one line |
msg("found_user:"+found_user); |
msg("found_user:" + found_user); |
At line 163 changed one line |
info.put("action","success"); |
info.put("action", "success"); |
At line 166 changed one line |
|
|
At line 170 changed 5 lines |
msg("getResult..url:"+url); |
msg("getResult...post_data:"+post_data); |
msg("getResult...username:"+username); |
msg("getResult...password:"+stars.substring(0,password.length())); |
HttpURLConnection connection = (HttpURLConnection)new URL(url).openConnection(); |
msg("getResult..url:" + url); |
msg("getResult...post_data:" + post_data); |
msg("getResult...username:" + username); |
msg("getResult...password:" + stars.substring(0, password.length())); |
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); |
At line 179 changed one line |
String authString = username+":"+password; |
String authString = username + ":" + password; |
At line 187 changed one line |
connection.setRequestProperty("Content-Length", dataBytes.length+""); |
connection.setRequestProperty("Content-Length", dataBytes.length + ""); |
At line 194 changed one line |
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF8")); |
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF8")); |
At line 200 changed one line |
allData += decodedString+"\r\n"; |
allData += decodedString + "\r\n"; |
At line 209 changed one line |
msg("CheckLogin...username:"+username); |
msg("CheckLogin...username:" + username); |
At line 216 changed one line |
if (!settings.getProperty("usernameTemplate").equals("")) |
if (!settings.getProperty("usernameTemplate").equals("") && Common.V() < 6) |
At line 220 changed 2 lines |
Properties server_item = (Properties)info.get("server_item"); |
String listen_ip_port = server_item.getProperty("ip")+"_"+server_item.getProperty("port"); |
Properties server_item = (Properties) info.get("server_item"); |
String listen_ip_port = server_item.getProperty("ip") + "_" + server_item.getProperty("port"); |
At line 223 changed 13 lines |
msg("Searching for template user in server item:"+listen_ip_port); |
msg("Raw server_item:"+server_item); |
crushftp.user.UserTools ut = new crushftp.user.UserTools(); |
Properties p = Common.read_user_no_cache(listen_ip_port,settings.getProperty("usernameTemplate")); |
if (p == null) |
{ |
p = Common.read_user_no_cache(listen_ip_port2,settings.getProperty("usernameTemplate")); |
ut.turn_off_inherit_all(p,listen_ip_port2); |
} |
else |
{ |
ut.turn_off_inherit_all(p,listen_ip_port); |
} |
msg("Searching for template user in server item:" + listen_ip_port); |
msg("Raw server_item:" + server_item); |
Properties p = UserTools.ut.getUser(listen_ip_port, settings.getProperty("usernameTemplate"), false, false); |
if (p == null) p = UserTools.ut.getUser(listen_ip_port2, settings.getProperty("usernameTemplate"), false, false); |
At line 239 changed one line |
msg("Setting usernameTemplate's settings:"+p.size()); |
msg("Setting usernameTemplate's settings:" + p.size()); |
At line 243 changed one line |
if (!key.equalsIgnoreCase("username") && !key.equalsIgnoreCase("user_name") && !key.equalsIgnoreCase("password")) try{user.put(key,p.get(key));}catch(Exception e){} |
if (!key.equalsIgnoreCase("username") && !key.equalsIgnoreCase("user_name") && !key.equalsIgnoreCase("password")) try |
{ |
user.put(key, p.get(key)); |
} |
catch (Exception e) |
{ |
} |
At line 245 changed one line |
if (user.getProperty("max_logins","0").equals("-1")) user.put("max_logins","0"); |
if (user.getProperty("max_logins", "0").equals("-1")) user.put("max_logins", "0"); |
At line 251 changed one line |
catch(Exception e)//no default user found, use template instead. |
catch (Exception e)//no default user found, use template instead. |
At line 256 changed one line |
user.put("virtualUser","true"); |
user.put("virtualUser", "true"); |
At line 338 added one line |
|
At line 266 changed one line |
if (settings.getProperty("debug").equals("true")) crushftp.handlers.Common.debug(0,"WebApplication:"+s); |
if (settings.getProperty("debug").equals("true")) crushftp.handlers.Common.debug(0, "WebApplication:" + s); |
At line 271 changed one line |
if (settings.getProperty("debug").equals("true")) crushftp.handlers.Common.debug(0,e); |
if (settings.getProperty("debug").equals("true")) crushftp.handlers.Common.debug(0, e); |
At line 274 removed one line |
|
At line 277 removed 213 lines |
---- |
GUI.java |
---- |
%%prettify |
{{{ |
package WebApplication; |
|
import java.awt.*; |
import javax.swing.*; |
import java.util.*; |
|
public class GUI extends JPanel |
{ |
private JTextField username = new JTextField(); |
private JLabel jLabel2 = new JLabel(); |
private JLabel jLabel3 = new JLabel(); |
private JPasswordField password = new JPasswordField(); |
private JLabel jLabel5 = new JLabel(); |
private JTextField user_url = new JTextField(); |
|
public GUI() |
{ |
try |
{ |
jbInit(); |
} |
catch(Exception ex) |
{ |
ex.printStackTrace(); |
} |
} |
|
transient Object parent = null; |
private JCheckBox enabled_cb = new JCheckBox(); |
private JCheckBox debug_cb = new JCheckBox(); |
private JPanel Settings = new JPanel(); |
private JComboBox server_item_combo = new JComboBox(); |
private JLabel jLabel8 = new JLabel(); |
private JTextField usernameTemplate_field = new JTextField(); |
private JCheckBox overwrite_vfs_cb = new JCheckBox(); |
private JLabel jLabel17 = new JLabel(); |
private JScrollPane LdapSettingsPane = new JScrollPane(); |
private JLabel jLabel6 = new JLabel(); |
private JTextField user_post_data = new JTextField(); |
private JLabel jLabel15 = new JLabel(); |
private JLabel jLabel18 = new JLabel(); |
private JTextField permissions_url = new JTextField(); |
private JTextField permissions_post_data = new JTextField(); |
private JLabel jLabel111 = new JLabel(); |
private JTextField vfs_url = new JTextField(); |
private JTextField vfs_post_data = new JTextField(); |
private JLabel jLabel112 = new JLabel(); |
private JTextField method = new JTextField(); |
private JLabel jLabel4 = new JLabel(); |
public void setParent(Object parent) |
{ |
this.parent = parent; |
} |
|
void jbInit() throws Exception |
{ |
this.setLayout(null); |
username.setToolTipText("Variables are allowed such as %username% or %password%."); |
username.setBounds(new Rectangle(153, 38, 176, 22)); |
jLabel2.setBounds(new Rectangle(10, 36, 136, 29)); |
jLabel2.setText("HTTP Username:"); |
jLabel2.setFont(new java.awt.Font("Arial", 0, 12)); |
jLabel3.setFont(new java.awt.Font("Arial", 0, 12)); |
jLabel3.setText("HTTP Password :"); |
jLabel3.setBounds(new Rectangle(10, 64, 136, 29)); |
password.setToolTipText("Variables are allowed such as %username% or %password%."); |
password.setBounds(new Rectangle(153, 66, 176, 22)); |
jLabel5.setFont(new java.awt.Font("Arial", 0, 12)); |
jLabel5.setText("User XML URL:"); |
jLabel5.setBounds(new Rectangle(10, 93, 147, 29)); |
user_url.setBounds(new Rectangle(153, 95, 372, 22)); |
server_item_combo.setToolTipText("Allows you to specify the individual server that can use this plugin " + |
"instance."); |
server_item_combo.setBounds(new Rectangle(153, 7, 183, 27)); |
enabled_cb.setText("Enabled"); |
enabled_cb.setBounds(new Rectangle(15, 1, 81, 22)); |
debug_cb.setText("Debug"); |
debug_cb.setBounds(new Rectangle(93, 0, 100, 22)); |
Settings.setBorder(BorderFactory.createEtchedBorder()); |
Settings.setPreferredSize(new Dimension(1, 330)); |
Settings.setLayout(null); |
jLabel8.setBounds(new Rectangle(10, 287, 119, 38)); |
jLabel8.setText("<html><body>Import settings from CrushFTP user:</body></html>"); |
jLabel8.setVerticalAlignment(SwingConstants.TOP); |
jLabel8.setFont(new java.awt.Font("Arial", 0, 12)); |
usernameTemplate_field.setBounds(new Rectangle(153, 289, 149, 22)); |
overwrite_vfs_cb.setBounds(new Rectangle(10, 317, 175, 22)); |
overwrite_vfs_cb.setToolTipText("For mutliple logins with the same username, keep this setting off."); |
overwrite_vfs_cb.setText("Overwrite VFS items?"); |
jLabel17.setFont(new java.awt.Font("Arial", 0, 12)); |
jLabel17.setText("Allowed Server Port:"); |
jLabel17.setBounds(new Rectangle(10, 6, 136, 29)); |
LdapSettingsPane.setBounds(new Rectangle(9, 28, 555, 398)); |
jLabel6.setBounds(new Rectangle(10, 120, 147, 29)); |
jLabel6.setText("Post Data:"); |
jLabel6.setFont(new java.awt.Font("Arial", 0, 12)); |
user_post_data.setBounds(new Rectangle(153, 122, 372, 22)); |
jLabel15.setBounds(new Rectangle(10, 149, 147, 29)); |
jLabel15.setText("Permissions XML URL:"); |
jLabel15.setFont(new java.awt.Font("Arial", 0, 12)); |
jLabel18.setFont(new java.awt.Font("Arial", 0, 12)); |
jLabel18.setText("Post Data:"); |
jLabel18.setBounds(new Rectangle(10, 176, 147, 29)); |
permissions_url.setBounds(new Rectangle(153, 151, 372, 22)); |
permissions_post_data.setBounds(new Rectangle(153, 178, 372, 22)); |
jLabel111.setBounds(new Rectangle(10, 231, 147, 29)); |
jLabel111.setText("Post Data:"); |
jLabel111.setFont(new java.awt.Font("Arial", 0, 12)); |
vfs_url.setBounds(new Rectangle(153, 206, 372, 22)); |
vfs_post_data.setBounds(new Rectangle(153, 233, 372, 22)); |
jLabel112.setFont(new java.awt.Font("Arial", 0, 12)); |
jLabel112.setText("VFS XML URL:"); |
jLabel112.setBounds(new Rectangle(10, 204, 147, 29)); |
method.setBounds(new Rectangle(153, 261, 82, 22)); |
method.setToolTipText("GET or POST"); |
jLabel4.setFont(new java.awt.Font("Arial", 0, 12)); |
jLabel4.setText("HTTP Method:"); |
jLabel4.setBounds(new Rectangle(10, 259, 136, 29)); |
Settings.add(jLabel17, null); |
Settings.add(server_item_combo, null); |
Settings.add(username, null); |
Settings.add(jLabel2, null); |
Settings.add(user_url, null); |
Settings.add(jLabel5, null); |
Settings.add(password, null); |
Settings.add(jLabel3, null); |
this.add(debug_cb, null); |
this.add(enabled_cb, null); |
this.add(LdapSettingsPane, null); |
LdapSettingsPane.getViewport().add(Settings, "LDAP Settings"); |
Settings.add(user_post_data, null); |
Settings.add(jLabel6, null); |
Settings.add(permissions_post_data, null); |
Settings.add(jLabel18, null); |
Settings.add(jLabel15, null); |
Settings.add(permissions_url, null); |
Settings.add(vfs_post_data, null); |
Settings.add(jLabel111, null); |
Settings.add(jLabel112, null); |
Settings.add(vfs_url, null); |
Settings.add(method, null); |
Settings.add(jLabel4, null); |
Settings.add(usernameTemplate_field, null); |
Settings.add(overwrite_vfs_cb, null); |
Settings.add(jLabel8, null); |
buildServerList(); |
} |
|
public Properties getSettings() |
{ |
Properties p = new Properties(); |
p.put("enabled",enabled_cb.isSelected()+""); |
p.put("debug",debug_cb.isSelected()+""); |
p.put("user_url",user_url.getText()); |
p.put("user_post_data",user_post_data.getText()); |
p.put("permissions_url",permissions_url.getText()); |
p.put("permissions_post_data",permissions_post_data.getText()); |
p.put("vfs_url",vfs_url.getText()); |
p.put("vfs_post_data",vfs_post_data.getText()); |
p.put("username",username.getText()); |
p.put("password",new crushftp.handlers.Common().encode_pass(new String(password.getPassword()),"DES")); |
p.put("usernameTemplate",usernameTemplate_field.getText()); |
p.put("overwrite_vfs",overwrite_vfs_cb.isSelected()+""); |
p.put("method",method.getText()); |
p.put("server_item",server_item_combo.getSelectedItem()); |
return p; |
} |
|
public void setSettings(Properties p) |
{ |
enabled_cb.setSelected(p.getProperty("enabled").equalsIgnoreCase("true")); |
debug_cb.setSelected(p.getProperty("debug").equalsIgnoreCase("true")); |
method.setText(p.getProperty("method")); |
username.setText(p.getProperty("username")); |
password.setText(new crushftp.handlers.Common().decode_pass(p.getProperty("password"))); |
user_url.setText(p.getProperty("user_url")); |
user_post_data.setText(p.getProperty("user_post_data")); |
permissions_url.setText(p.getProperty("permissions_url")); |
permissions_post_data.setText(p.getProperty("permissions_post_data")); |
vfs_url.setText(p.getProperty("vfs_url")); |
vfs_post_data.setText(p.getProperty("vfs_post_data")); |
usernameTemplate_field.setText(p.getProperty("usernameTemplate")); |
overwrite_vfs_cb.setSelected(p.getProperty("overwrite_vfs").equalsIgnoreCase("true")); |
server_item_combo.setSelectedItem(p.getProperty("server_item")); |
} |
|
public void buildServerList() |
{ |
server_item_combo.removeAllItems(); |
server_item_combo.addItem("All"); |
try |
{ |
Vector the_server_list = (Vector)crushftp.server.ServerStatus.server_settings.get("server_list"); |
for (int x=0; x<the_server_list.size(); x++) |
{ |
Properties server_item = (Properties)((Properties)the_server_list.elementAt(x)).clone(); |
server_item_combo.addItem(server_item.getProperty("ip","lookup")+"_"+server_item.getProperty("port","21")); |
} |
} |
catch(Exception e) |
{ |
e.printStackTrace(); |
} |
} |
|
} |
}}} |
/% |