/**
* Copyright (c) 2007, Markus Jevring <markus@jevring.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the contributors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/**
* Copyright (c) 2005, Markus Jevring <markus@jevring.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the contributors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
package cu.ftpd.user.userbases;
import cu.ftpd.user.User;
import cu.ftpd.user.UserPermission;
import cu.ftpd.user.groups.Group;
import cu.ftpd.user.groups.GroupExistsException;
import cu.ftpd.user.groups.NoSuchGroupException;
import cu.ftpd.user.statistics.StatisticsEntry;
import cu.ftpd.user.statistics.UserStatistics;
import cu.ftpd.user.statistics.local.LocalUserStatistics;
import cu.ftpd.user.userbases.local.LocalUser;
import cu.ftpd.user.userbases.local.LocalUserbase;
import cu.ftpd.user.userbases.UserExistsException;
import java.io.*;
import java.util.HashMap;
/**
* This class converts a glftpd 2.01 userbase to a cuftpd userbase.
*
* @author Markus Jevring <markus@jevring.net>
* @since 2007-12-15 - 21:00:00
* @version $Id: UserbaseConverter.java 258 2008-10-26 12:47:23Z jevring $
*/
public class UserbaseConverter {
/*
get the location of the glftpd root installation directory
get the location of the data directory
read /etc/passwd
store user->password hash
read userfiles
convert each entry to a cuftpd userfile entry
convert the statistics to userstatistics entries
insert the hashed password
read general groupfile
read groupfiles
for each groupfile, create a line in our groupfile
*/
private final HashMap<String, String> passwords = new HashMap<String, String>();
private final LocalUserbase userbase;
private final File glftpdRoot;
private final LocalUserStatistics statistics;
public UserbaseConverter(String glftpdRoot, String cuftpdDataDirectory) throws IOException {
final File dataDirectory = new File(cuftpdDataDirectory);
dataDirectory.mkdirs();
new File(dataDirectory, "/users").mkdirs();
final File statisticsDir = new File(dataDirectory, "/logs/userstatistics");
statisticsDir.mkdirs();
userbase = new LocalUserbase(dataDirectory, false, null);
statistics = new LocalUserStatistics(statisticsDir);
this.glftpdRoot = new File(glftpdRoot);
}
public void convert() {
try {
readPasswords();
} catch (IOException e) {
System.err.println("Failed to read glftpd passwd file: " + e.getMessage());
}
createUsers();
try {
createGroups();
} catch (IOException e) {
System.err.println("Failed to read groupfile: " + e.getMessage());
}
}
private void createGroups() throws IOException {
// read the groups-file, and for each group, read the correspoding groupfile, and create a cuftpd group with the same name, description, and slots (and allotment)
BufferedReader in = null;
try {
File groupfile = new File(glftpdRoot, "/etc/group");
in = new BufferedReader(new InputStreamReader(new FileInputStream(groupfile)));
String line;
while ((line = in.readLine()) != null) {
String[] parts = line.split(":");
Group g = null;
try {
g = userbase.createGroup(parts[0], parts[1]);
} catch (GroupExistsException e) {
System.err.println(e.getMessage());
System.err.println("skipping...");
continue;
}
File detailedGroupfile = new File(glftpdRoot, "/ftp-data/groups/" + g.getName());
try {
fillGroup(g, detailedGroupfile);
} catch (IOException e) {
System.err.println("Failed to read groupfile: " + e.getMessage());
}
}
userbase.saveGroups();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void fillGroup(Group group, File groupfile) throws IOException {
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream(groupfile)));
String line;
while ((line = in.readLine()) != null) {
if (line.startsWith("SLOTS")) {
String[] parts = line.substring(5).trim().split(" ");
// SLOTS, LEECH SLOTS, ALLOTMENT SLOTS, MAX. ALLOTMENT SIZE
group.setSlots(Integer.parseInt(parts[0]));
group.setLeechSlots(Integer.parseInt(parts[1]));
group.setAllotmentSlots(Integer.parseInt(parts[2]));
group.setMaxAllotment(Integer.parseInt(parts[3]));
}
}
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void createUsers() {
File userdir = new File(glftpdRoot, "/ftp-data/users");
File[] users = userdir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
// don't include users glftpd or default.user
return (!"glftpd".equals(name) && !"default.user".equals(name));
}
});
for (int i = 0; i < users.length; i++) {
File user = users[i];
try {
addUser(user);
} catch (IOException e) {
System.err.println("Could not read userfile for " + user.getName());
} catch (NoSuchUserException e) {
System.err.println(e.getMessage());
} catch (NoSuchGroupException e) {
System.err.println(e.getMessage());
} catch (UserExistsException e) {
System.err.println(e.getMessage());
}
}
}
private void addUser(File userfile) throws IOException, UserExistsException, NoSuchUserException, NoSuchGroupException {
// read the userfile, and for each value, add the corresponding value to a cuftpd user
if (!userfile.getName().matches("[\\w-]+")) {
System.err.println("Illegal username: " + userfile.getName());
return;
}
LocalUser user = userbase.addUser(userfile.getName(), "WILL_BE_CHANGED", false);
user.passwd(passwords.get(user.getUsername()), false);
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream(userfile)));
String line;
boolean hasPrimaryGroupSet = false;
while ((line = in.readLine()) != null) {
if (line.startsWith("RATIO")) {
String[] parts = line.substring(5).trim().split(" ");
if ("0".equals(parts[0].trim())) {
userbase.setLeechForUser(true, user.getUsername(), null, false);
}
} else if (line.startsWith("CREDITS")) {
String[] parts = line.substring(7).trim().split(" ");
user.setCredits(Long.parseLong(parts[0].trim()), false);
} else if (line.startsWith("TAGLINE")) {
user.setTagline(line.substring(7).trim(), false);
} else if (line.startsWith("TIME")) {
String[] parts = line.substring(4).trim().split(" ");
user.setLastlog(Long.parseLong(parts[1].trim()), false);
} else if (line.startsWith("ADDED")) {
/* _todo: we can't set created from the inside. we'll let them be created when the script is run then instead
String[] parts = line.substring(5).trim().split(" ");
user.setCreated(line.substring(5).trim());
*/
} else if (line.startsWith("IP")) {
user.addIp(line.substring(2).trim(), false);
} else if (line.startsWith("DNS")) {
user.addIp(line.substring(3).trim(), false);
} else if (line.startsWith("LOGINS")) {
String[] parts = line.substring(6).trim().split(" ");
user.setLastlog(Long.parseLong(parts[0].trim()), false);
} else if (line.startsWith("GENERAL")) {
String[] parts = line.substring(7).trim().split(" ");
userbase.setAllotmentForUser(Long.parseLong(parts[0].substring(parts[0].indexOf(',') + 1)), user.getUsername(), null, false);
} else if (line.startsWith("FLAGS")) {
String flags = line.substring(5);
if (flags.contains("D")) {
user.addPermission(UserPermission.KICK, false);
}
if (flags.contains("F") || flags.contains("G")) {
user.addPermission(UserPermission.CREDITS, false);
}
if (flags.contains("H")) {
user.addPermission(UserPermission.VIEWUSER, false);
}
// flag D = UserPermission.KICK
// flag F | G = UserPermission.CREDITS
// flag H = UserPermission.VIEWUSER
} else if (line.startsWith("ALLUP")) {
// _todo: find out the real format for stats
// one "set" of values for each section
StatisticsEntry se = statistics.getUserStatistics(user.getUsername(), "default");
String[] parts = line.substring(5).trim().split(" ");
se.set(UserStatistics.ALLUP_FILES, Long.parseLong(parts[0]));
se.set(UserStatistics.ALLUP_BYTES, Long.parseLong(parts[1]) / 1024);
se.set(UserStatistics.ALLUP_TIME, Long.parseLong(parts[2]) / 1000);
statistics.store(se);
} else if (line.startsWith("ALLDN")) {
StatisticsEntry se = statistics.getUserStatistics(user.getUsername(), "default");
String[] parts = line.substring(5).trim().split(" ");
se.set(UserStatistics.ALLDN_FILES, Long.parseLong(parts[0]));
se.set(UserStatistics.ALLDN_BYTES, Long.parseLong(parts[1]) / 1024);
se.set(UserStatistics.ALLUP_TIME, Long.parseLong(parts[2]) / 1000);
statistics.store(se);
} else if (line.startsWith("MNUP")) {
StatisticsEntry se = statistics.getUserStatistics(user.getUsername(), "default");
String[] parts = line.substring(5).trim().split(" ");
se.set(UserStatistics.MNUP_FILES, Long.parseLong(parts[0]));
se.set(UserStatistics.MNUP_BYTES, Long.parseLong(parts[1]) / 1024);
se.set(UserStatistics.MNUP_TIME, Long.parseLong(parts[2]) / 1000);
statistics.store(se);
} else if (line.startsWith("MNDN")) {
StatisticsEntry se = statistics.getUserStatistics(user.getUsername(), "default");
String[] parts = line.substring(5).trim().split(" ");
se.set(UserStatistics.MNDN_FILES, Long.parseLong(parts[0]));
se.set(UserStatistics.MNDN_BYTES, Long.parseLong(parts[1]) / 1024);
se.set(UserStatistics.MNUP_TIME, Long.parseLong(parts[2]) / 1000);
statistics.store(se);
} else if (line.startsWith("WKUP")) {
StatisticsEntry se = statistics.getUserStatistics(user.getUsername(), "default");
String[] parts = line.substring(5).trim().split(" ");
se.set(UserStatistics.WKUP_FILES, Long.parseLong(parts[0]));
se.set(UserStatistics.WKUP_BYTES, Long.parseLong(parts[1]) / 1024);
se.set(UserStatistics.WKUP_TIME, Long.parseLong(parts[2]) / 1000);
statistics.store(se);
} else if (line.startsWith("WKDN")) {
StatisticsEntry se = statistics.getUserStatistics(user.getUsername(), "default");
String[] parts = line.substring(5).trim().split(" ");
se.set(UserStatistics.WKDN_FILES, Long.parseLong(parts[0]));
se.set(UserStatistics.WKDN_BYTES, Long.parseLong(parts[1]) / 1024);
se.set(UserStatistics.WKUP_TIME, Long.parseLong(parts[2]) / 1000);
statistics.store(se);
} else if (line.startsWith("DAYUP")) {
StatisticsEntry se = statistics.getUserStatistics(user.getUsername(), "default");
String[] parts = line.substring(5).trim().split(" ");
se.set(UserStatistics.DAYUP_FILES, Long.parseLong(parts[0]));
se.set(UserStatistics.DAYUP_BYTES, Long.parseLong(parts[1]) / 1024);
se.set(UserStatistics.DAYUP_TIME, Long.parseLong(parts[2]) / 1000);
statistics.store(se);
} else if (line.startsWith("DAYDN")) {
StatisticsEntry se = statistics.getUserStatistics(user.getUsername(), "default");
String[] parts = line.substring(5).trim().split(" ");
se.set(UserStatistics.DAYDN_FILES, Long.parseLong(parts[0]));
se.set(UserStatistics.DAYDN_BYTES, Long.parseLong(parts[1]) / 1024);
se.set(UserStatistics.DAYUP_TIME, Long.parseLong(parts[2]) / 1000);
statistics.store(se);
} else if (line.startsWith("GROUP")) {
String[] groupdata = line.substring(5).trim().split(" ", 2);
final String group = groupdata[0].trim();
user.addGroup(group);
if ("1".equals(groupdata[1].trim())) {
user.addGadminForGroup(group, false);
}
if (!hasPrimaryGroupSet) {
hasPrimaryGroupSet = true;
user.setPrimaryGroup(group, false);
}
}
}
} finally {
if (in != null) {
in.close();
}
}
}
private void readPasswords() throws IOException {
BufferedReader passwords = new BufferedReader(new InputStreamReader(new FileInputStream(new File(glftpdRoot, "/etc/passwd"))));
String password;
while ((password = passwords.readLine()) != null) {
String[] parts = password.split(":");
this.passwords.put(parts[0], parts[1]);
}
passwords.close();
}
public static void main(String[] args) {
if (args.length == 2) {
System.out.println("glftpd dir: " + args[0]);
System.out.println("cuftpd data dir: " + args[1]);
System.out.println("NOTE: this will only process the first section in the userfiles.");
UserbaseConverter uc = null;
try {
uc = new UserbaseConverter(args[0], args[1]);
uc.convert();
System.out.println("Conversion complete.");
} catch (IOException e) {
System.err.println("Conversion FAILED");
System.err.println(e.getMessage());
}
} else {
System.out.println("Use: java -jar converter.jar glftpd_root_path cuftpd_data_directory");
}
}
}