UserAdmin.java 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. package jplagWebService.serverAccess;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileWriter;
  5. import java.io.PrintWriter;
  6. import java.text.DateFormat;
  7. import java.util.Calendar;
  8. import java.util.Date;
  9. import java.util.GregorianCalendar;
  10. import java.util.HashMap;
  11. import java.util.Locale;
  12. import java.util.Properties;
  13. import java.util.TimeZone;
  14. import java.util.regex.Pattern;
  15. import javax.mail.Authenticator;
  16. import javax.mail.Message;
  17. import javax.mail.PasswordAuthentication;
  18. import javax.mail.Session;
  19. import javax.mail.Transport;
  20. import javax.mail.internet.InternetAddress;
  21. import javax.mail.internet.MimeMessage;
  22. import javax.xml.parsers.DocumentBuilder;
  23. import javax.xml.parsers.DocumentBuilderFactory;
  24. import javax.xml.transform.Result;
  25. import javax.xml.transform.Source;
  26. import javax.xml.transform.Transformer;
  27. import javax.xml.transform.TransformerConfigurationException;
  28. import javax.xml.transform.TransformerException;
  29. import javax.xml.transform.TransformerFactory;
  30. import javax.xml.transform.dom.DOMSource;
  31. import javax.xml.transform.stream.StreamResult;
  32. import jplagUtils.PropertiesLoader;
  33. import jplagWebService.server.FinishRequestData;
  34. import jplagWebService.server.JPlagException;
  35. import jplagWebService.server.MailTemplate;
  36. import jplagWebService.server.RequestData;
  37. import jplagWebService.server.UserData;
  38. import jplagWebService.server.UserDataArray;
  39. import jplagWebService.server.UserInfo;
  40. import jplagWebService.serverImpl.JPlagCentral;
  41. import org.w3c.dom.Document;
  42. import org.w3c.dom.Element;
  43. import org.w3c.dom.NodeList;
  44. public class UserAdmin extends Thread {
  45. private static final long ONEDAY = 86400000;
  46. private boolean doStopUserAdmin = false;
  47. public static final int USER_INVALID = 0;
  48. public static final int USER_NORMAL = 1;
  49. // public static final int USER_EXPIRED = 5;
  50. // public static final int USER_DEACTIVATED = 10;
  51. public static final int USER_GROUPADMIN = 64;
  52. public static final int USER_JPLAGADMIN = 192;
  53. public static final int USER_JPLAGADMINNOTIFY = 224; // (email notify)
  54. public static final int USER_SERVERPAGE = 256;
  55. public static final int MASK_DEVELOPER = 2;
  56. public static final int MASK_EXPIRED = 4;
  57. public static final int MASK_DEACTIVATED = 8;
  58. public static final int MASK_REQUESTNOTIFY = 32;
  59. public static final int MASK_ANYADMIN = 64;
  60. public static final int MASK_JPLAGADMIN = 128;
  61. public static final int MASK_SERVERPAGE = 256;
  62. public static final int MASK_NOAUTOASKEXTEND = 512;
  63. public static final String JPLAG_SERVER = "https://jplag.ipd.kit.edu/";
  64. private Document doc = null;
  65. private Element rootElement = null;
  66. private File userFile = null;
  67. private PrintWriter logWriter = null;
  68. private PrintWriter mailLogWriter = null;
  69. private HashMap<String, PassAndState> userPassList = new HashMap<String, PassAndState>();
  70. private Object userPassListMutex = new Object();
  71. private class PassAndState {
  72. public String password;
  73. public int state;
  74. public PassAndState(String pass, int stat) {
  75. password = pass;
  76. state = stat;
  77. }
  78. }
  79. private RequestAdmin requestAdmin = null;
  80. public UserAdmin(String jplagHome) {
  81. requestAdmin = new RequestAdmin(jplagHome);
  82. userFile = new File(jplagHome + File.separator + "jplag_users.xml");
  83. if (!userFile.exists())
  84. create();
  85. else
  86. parse();
  87. try {
  88. logWriter = new PrintWriter(new FileWriter(new File(jplagHome + File.separator + "userAdmin.log"), true), true);
  89. mailLogWriter = new PrintWriter(new FileWriter(new File(jplagHome + File.separator + "userAdminMail.log"), true), true);
  90. } catch (java.io.IOException ex) {
  91. System.out.println("UserAdmin: Unable to open log files!");
  92. }
  93. }
  94. public RequestAdmin getRequestAdmin() {
  95. return requestAdmin;
  96. }
  97. private synchronized void log(String str) {
  98. if (logWriter != null)
  99. logWriter.println(str);
  100. }
  101. /**
  102. * Writes the "jplag-users" data to an XML file
  103. */
  104. private synchronized void writeXMLFile() {
  105. try {
  106. // Prepare the DOM document for writing
  107. Source source = new DOMSource(doc);
  108. // Prepare the output file
  109. Result result = new StreamResult(userFile);
  110. // Write the DOM document to the file
  111. Transformer xformer = TransformerFactory.newInstance().newTransformer();
  112. xformer.transform(source, result);
  113. } catch (TransformerConfigurationException e) {
  114. e.printStackTrace();
  115. } catch (TransformerException e) {
  116. e.printStackTrace();
  117. }
  118. }
  119. /**
  120. * Creates a new "jplag-user database" XML file
  121. */
  122. private void create() {
  123. DocumentBuilderFactory docBFac;
  124. DocumentBuilder docBuild;
  125. try {
  126. docBFac = DocumentBuilderFactory.newInstance();
  127. docBuild = docBFac.newDocumentBuilder();
  128. doc = docBuild.newDocument();
  129. } catch (Exception ex) {
  130. ex.printStackTrace();
  131. return;
  132. }
  133. rootElement = doc.createElement("jplag-users");
  134. doc.appendChild(rootElement);
  135. writeXMLFile();
  136. }
  137. /**
  138. * Loads an existing "jplag-user database" into memory
  139. */
  140. private void parse() {
  141. try {
  142. FileInputStream xmlStream = new FileInputStream(userFile);
  143. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  144. factory.setIgnoringComments(true);
  145. DocumentBuilder builder = factory.newDocumentBuilder();
  146. doc = builder.parse(xmlStream);
  147. xmlStream.close();
  148. rootElement = doc.getDocumentElement();
  149. NodeList userList = rootElement.getElementsByTagName("user");
  150. System.out.println("Number of users: " + userList.getLength());
  151. synchronized (userPassListMutex) {
  152. // avoid rehash operations during initialisation
  153. userPassList = new HashMap<String, PassAndState>((int) (userList.getLength() * 1.4));
  154. for (int j = 0; j < userList.getLength(); j++) {
  155. Element user = (Element) userList.item(j);
  156. userPassList.put(user.getAttribute("username"),
  157. new PassAndState(user.getAttribute("password"), parseInt(user, "state")));
  158. }
  159. }
  160. } catch (javax.xml.parsers.ParserConfigurationException e) {
  161. // throw new RuntimeException("Failed to create DocumentBuilder");
  162. e.printStackTrace();
  163. } catch (org.xml.sax.SAXException e) {
  164. // throw new RuntimeException("Error parsing users.xml");
  165. e.printStackTrace();
  166. } catch (Exception e) {
  167. System.out.println("File error!");
  168. e.printStackTrace();
  169. }
  170. }
  171. /**
  172. * Checks whether the given user already exists
  173. */
  174. public boolean exists(String username) {
  175. boolean ret;
  176. synchronized (userPassListMutex) {
  177. ret = userPassList.containsKey(username);
  178. }
  179. return ret;
  180. }
  181. /**
  182. * Checks whether the given username is valid
  183. */
  184. public boolean isValidUsername(String username) {
  185. return Pattern.matches("^[\\w\\@\\.]+$", username);
  186. }
  187. public int getLoginState(String username, String password) {
  188. synchronized (userPassListMutex) {
  189. if (userPassList.containsKey(username)) {
  190. PassAndState pas = (PassAndState) userPassList.get(username);
  191. if (!pas.password.equals(password))
  192. return USER_INVALID;
  193. return pas.state;
  194. }
  195. }
  196. return USER_INVALID;
  197. }
  198. /**
  199. * Returns the user XML element for the given username
  200. */
  201. private Element getUserXMLElement(String username) {
  202. NodeList userList = rootElement.getElementsByTagName("user");
  203. for (int i = 0; i < userList.getLength(); i++) {
  204. Element elem = (Element) userList.item(i);
  205. if (elem.getAttribute("username").equals(username))
  206. return elem;
  207. }
  208. return null;
  209. }
  210. private void setString(Element elem, String attrname, String str) {
  211. String old = elem.getAttribute(attrname);
  212. if (old.equals(str))
  213. return;
  214. log(" " + attrname + ": " + old + " -> " + str);
  215. elem.setAttribute(attrname, str);
  216. }
  217. private void setNillableString(Element elem, String attrname, String str) {
  218. if (str == null || str.length() == 0) {
  219. if (elem.hasAttribute(attrname)) {
  220. log(" " + attrname + ": " + elem.getAttribute(attrname) + " gets deleted");
  221. elem.removeAttribute(attrname);
  222. }
  223. } else
  224. setString(elem, attrname, str);
  225. }
  226. private String parseNillableString(Element elem, String attrname) {
  227. String str = elem.getAttribute(attrname);
  228. if (str.length() == 0)
  229. return null;
  230. else
  231. return str;
  232. }
  233. private static String formatCalendar(Calendar cal) {
  234. DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.GERMAN);
  235. return df.format(cal.getTime());
  236. }
  237. private void setNillableCalendar(Element user, String attr, Calendar cal) {
  238. setNillableString(user, attr, (cal == null) ? null : formatCalendar(cal));
  239. }
  240. private static Calendar parseCalendar(String str) {
  241. Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
  242. try {
  243. cal.setTime(DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.GERMAN).parse(str));
  244. } catch (java.text.ParseException ex) {
  245. System.out.println("Illegal date: \"" + str + "\"");
  246. cal.set(1970, 0, 1); // set an error date (1.1.1970)
  247. System.out.println("Set to " + formatCalendar(cal));
  248. }
  249. return cal;
  250. }
  251. private static Calendar parseCalendar(Element elem, String attrname) {
  252. return parseCalendar(elem.getAttribute(attrname));
  253. }
  254. private static Calendar parseNillableCalendar(String str) {
  255. if (str.length() == 0)
  256. return null;
  257. else
  258. return parseCalendar(str);
  259. }
  260. public static Calendar parseNillableCalendar(Element elem, String attrname) {
  261. return parseNillableCalendar(elem.getAttribute(attrname));
  262. }
  263. public static int parseInt(Element elem, String attrname) {
  264. int val;
  265. try {
  266. val = Integer.parseInt(elem.getAttribute(attrname));
  267. } catch (NumberFormatException ex) {
  268. System.out.println("Illegal " + attrname + " int: \"" + elem.getAttribute(attrname) + "\"");
  269. val = -1;
  270. }
  271. return val;
  272. }
  273. public synchronized int getState(String name) {
  274. Element user = getUserXMLElement(name);
  275. int val = parseInt(user, "state");
  276. if (val < 0)
  277. return USER_INVALID;
  278. else
  279. return val;
  280. }
  281. public synchronized UserInfo getUserInfo(String username, int leftSlots) {
  282. Element elem = getUserXMLElement(username);
  283. Calendar expires = parseNillableCalendar(elem, "expires");
  284. String email = elem.getAttribute("email");
  285. String emailSecond = parseNillableString(elem, "emailSecond");
  286. String homepage = parseNillableString(elem, "homepage");
  287. return new UserInfo(leftSlots, expires, email, emailSecond, homepage);
  288. }
  289. public synchronized void updateUserInfo(String username, String newpassword, String newemailsecond, String newhomepage)
  290. throws JPlagException {
  291. Element elem = (Element) getUserXMLElement(username);
  292. log("[" + new Date() + "] " + username + " called updateUserInfo:");
  293. if (newpassword != null) {
  294. if (newpassword.length() < 6)
  295. throw new JPlagException("updateUserInfo", "The password is too short!", "The password must have at least 6 characters!");
  296. setString(elem, "password", newpassword);
  297. synchronized (userPassListMutex) {
  298. userPassList.put(username, new PassAndState(newpassword, parseInt(elem, "state")));
  299. }
  300. }
  301. if (newemailsecond != null)
  302. setNillableString(elem, "emailSecond", newemailsecond);
  303. if (newhomepage != null)
  304. setNillableString(elem, "homepage", newhomepage);
  305. writeXMLFile();
  306. }
  307. /**
  308. * If isGroupAdmin is false, it returns a list of all users.<br>
  309. * If isGroupAdmin is true, it returns a list of all users he created with
  310. * the password set to an empty string.<br>
  311. *
  312. * @return A list of userdata elements in form of an ArrayOfUserData element
  313. */
  314. public synchronized UserDataArray getUserDataArray(String calling_username, boolean isGroupAdmin) throws JPlagException {
  315. NodeList userList = rootElement.getElementsByTagName("user");
  316. int num;
  317. if (isGroupAdmin) {
  318. num = 0;
  319. for (int i = 0; i < userList.getLength(); i++)
  320. if (((Element) userList.item(i)).getAttribute("createdBy").equals(calling_username))
  321. num++;
  322. } else
  323. num = userList.getLength();
  324. UserData[] data = new UserData[num];
  325. for (int i = 0, j = 0; i < userList.getLength(); i++) {
  326. Element elem = (Element) userList.item(i);
  327. if (!isGroupAdmin || elem.getAttribute("createdBy").equals(calling_username)) {
  328. data[j] = new UserData();
  329. data[j].setUsername(elem.getAttribute("username"));
  330. data[j].setPassword(isGroupAdmin ? "" : elem.getAttribute("password"));
  331. data[j].setCreated(parseCalendar(elem, "created"));
  332. data[j].setCreatedBy(elem.getAttribute("createdBy"));
  333. data[j].setExpires(parseNillableCalendar(elem, "expires"));
  334. data[j].setLastUsage(parseNillableCalendar(elem, "lastUsage"));
  335. data[j].setNumOfSubs(parseInt(elem, "numOfSubs"));
  336. data[j].setRealName(elem.getAttribute("realname"));
  337. data[j].setEmail(elem.getAttribute("email"));
  338. data[j].setEmailSecond(parseNillableString(elem, "emailSecond"));
  339. data[j].setHomepage(parseNillableString(elem, "homepage"));
  340. data[j].setReason(elem.getAttribute("reason"));
  341. data[j].setNotes(parseNillableString(elem, "notes"));
  342. data[j].setState(parseInt(elem, "state"));
  343. j++;
  344. }
  345. }
  346. return new UserDataArray(data);
  347. }
  348. /**
  349. * This function only creates a new user element. It does only initialise
  350. * "createdBy", "numOfSubs" and "state"!
  351. */
  352. private Element getNewUserElement(String creator) {
  353. Element newuser = doc.createElement("user");
  354. newuser.setAttribute("createdBy", creator);
  355. newuser.setAttribute("numOfSubs", "0");
  356. newuser.setAttribute("state", USER_NORMAL + "");
  357. Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
  358. newuser.setAttribute("created", formatCalendar(cal));
  359. return newuser;
  360. }
  361. /**
  362. * Sets userdata fields, adds a new user or deletes one. Group admins can
  363. * only change their "own" users and can not change the "createdBy" and
  364. * "state" fields. For JPlag admins the "createdBy" field has to point to a
  365. * valid user!
  366. */
  367. public synchronized void setUserData(UserData data, String oldUsername, String calling_username, boolean isGroupAdmin)
  368. throws JPlagException {
  369. Element user;
  370. log("[" + new Date() + "] " + calling_username + " called setUserData " + "for user " + oldUsername + " :");
  371. if (oldUsername == null) // create a new user?
  372. {
  373. if (data.getUsername() == null)
  374. throw new JPlagException("setUserData", "Illegal parameters! " + "oldUsername and username is null!", "Find out what "
  375. + "you want, create an user or delete one!");
  376. if (!isValidUsername(data.getUsername()))
  377. throw new JPlagException("setUserData", "Illegal new username!", "The username may only consist of the "
  378. + "following characters: 'A'-'Z', 'a'-'z', '0'-'9', " + "'@' and '.'");
  379. if (exists(data.getUsername()))
  380. throw new JPlagException("setUserData", "Adding a new user " + "with an already existing name!", "Try another name!");
  381. log(" " + calling_username + " generates new user " + data.getUsername());
  382. user = getNewUserElement(calling_username);
  383. } else
  384. user = getUserXMLElement(oldUsername);
  385. if (user == null)
  386. throw new JPlagException("setUserData", "User doesn't exist (anymore?)!", "Please update the user list!");
  387. if (data.getUsername() == null) // delete user username?
  388. {
  389. if (isGroupAdmin && !calling_username.equals(user.getAttribute("createdBy"))) {
  390. // log invalid access, wait a bit and throw an exception
  391. System.out.println("setUserData: group admin " + calling_username + " tried to delete user " + oldUsername + " on "
  392. + new Date());
  393. try {
  394. Thread.sleep(10);
  395. } catch (Exception ex) {
  396. }
  397. throw new JPlagException("setUserData", "You are not allowed" + "to delete this user!", "You can only delete users you"
  398. + " created! This has been logged.");
  399. }
  400. log(" deletes user " + oldUsername + " (real name: " + user.getAttribute("realname") + " email: " + user.getAttribute("email")
  401. + ")");
  402. synchronized (userPassListMutex) {
  403. userPassList.remove(oldUsername);
  404. }
  405. AccessStructure[] accStructs = JPlagCentral.listAccessStructures(oldUsername);
  406. for (int i = 0; i < accStructs.length; i++)
  407. JPlagCentral.cancelSubmission(accStructs[i]);
  408. rootElement.removeChild(user);
  409. } else {
  410. if (isGroupAdmin && !calling_username.equals(user.getAttribute("createdBy"))) {
  411. // log invalid access, wait a bit and throw an exception
  412. System.out.println("setUserData: group admin " + calling_username + " tried to change fields for user "
  413. + data.getUsername() + " on " + new Date());
  414. try {
  415. Thread.sleep(10);
  416. } catch (Exception ex) {
  417. }
  418. throw new JPlagException("setUserData", "You are not allowed" + "to change this user!", "You can only change users you"
  419. + " created! This has been logged.");
  420. }
  421. if (!user.getAttribute("username").equals(data.getUsername())) {
  422. if (!isValidUsername(data.getUsername()))
  423. throw new JPlagException("setUserData", "Illegal new username!", "The username may only consist of the "
  424. + "following characters: 'A'-'Z', 'a'-'z', " + "'0'-'9', '@' and '.'");
  425. if (exists(data.getUsername()))
  426. throw new JPlagException("setUserData", "Changing an username to an already existing name!", "Try another name!");
  427. setString(user, "username", data.getUsername());
  428. synchronized (userPassListMutex) {
  429. userPassList.remove(oldUsername);
  430. userPassList.put(data.getUsername(), new PassAndState(user.getAttribute("password"), parseInt(user, "state")));
  431. }
  432. }
  433. if (data.getPassword().length() != 0 && !user.getAttribute("password").equals(data.getPassword())) {
  434. setString(user, "password", data.getPassword());
  435. synchronized (userPassListMutex) {
  436. userPassList.put(user.getAttribute("username"), new PassAndState(data.getPassword(), parseInt(user, "state")));
  437. }
  438. }
  439. setNillableCalendar(user, "expires", data.getExpires());
  440. setString(user, "realname", data.getRealName());
  441. setString(user, "email", data.getEmail());
  442. setNillableString(user, "emailSecond", data.getEmailSecond());
  443. setNillableString(user, "homepage", data.getHomepage());
  444. setString(user, "reason", data.getReason());
  445. setNillableString(user, "notes", data.getNotes());
  446. if (!isGroupAdmin) {
  447. setString(user, "created", formatCalendar(data.getCreated()));
  448. setString(user, "createdBy", data.getCreatedBy());
  449. setNillableCalendar(user, "lastUsage", data.getLastUsage());
  450. setString(user, "numOfSubs", data.getNumOfSubs() + "");
  451. if (parseInt(user, "state") != data.getState()) {
  452. setString(user, "state", data.getState() + "");
  453. synchronized (userPassListMutex) {
  454. userPassList.put(user.getAttribute("username"), new PassAndState(user.getAttribute("password"), data.getState()));
  455. }
  456. }
  457. } else {
  458. if (parseInt(user, "state") != data.getState()) {
  459. switch (data.getState() & ~MASK_DEVELOPER) {
  460. case USER_NORMAL:
  461. case MASK_EXPIRED:
  462. case MASK_DEACTIVATED:
  463. setString(user, "state", data.getState() + "");
  464. synchronized (userPassListMutex) {
  465. userPassList.put(user.getAttribute("username"),
  466. new PassAndState(user.getAttribute("password"), data.getState()));
  467. }
  468. break;
  469. default:
  470. // log illegal state
  471. System.out.println("setUserData: group admin " + calling_username + " tried to set " + data.getUsername()
  472. + "'s state to " + data.getState() + " on " + new Date());
  473. break;
  474. }
  475. }
  476. }
  477. if (oldUsername == null) // create new user?
  478. rootElement.appendChild(user);
  479. checkExpired(user);
  480. }
  481. writeXMLFile();
  482. }
  483. private boolean updateCalendar(Element elem) {
  484. Calendar curcal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
  485. Calendar cal = parseCalendar(elem, "lastUsage");
  486. if (curcal.get(Calendar.DAY_OF_MONTH) != cal.get(Calendar.DAY_OF_MONTH) || curcal.get(Calendar.MONTH) != cal.get(Calendar.MONTH)
  487. || curcal.get(Calendar.YEAR) != cal.get(Calendar.YEAR)) {
  488. elem.setAttribute("lastUsage", formatCalendar(curcal));
  489. return true;
  490. }
  491. return false;
  492. }
  493. public synchronized void updateLastUsage(String username) {
  494. Element elem = getUserXMLElement(username);
  495. if (updateCalendar(elem))
  496. writeXMLFile();
  497. }
  498. public synchronized void incrementSubmissionCounter(String username) {
  499. Element elem = getUserXMLElement(username);
  500. int subs = parseInt(elem, "numOfSubs");
  501. elem.setAttribute("numOfSubs", (subs + 1) + "");
  502. updateCalendar(elem); // just in case a client runs a few months ;)
  503. writeXMLFile();
  504. }
  505. /**
  506. * Called by an AdminTool client from a user with administrator rights.
  507. * Accepts or declines a specified request
  508. */
  509. public synchronized boolean finishAccountRequest(FinishRequestData data, String adminname) throws JPlagException {
  510. RequestData rd = requestAdmin.getRequestData(data.getOldUsername());
  511. if (rd == null)
  512. throw new JPlagException("requestAccount", "Request doesn't exist (anymore)!",
  513. "Wrong username or this request has already been decided!");
  514. if (data.getPassword() != null) // user not dismissed?
  515. {
  516. // User accepted, add to user database
  517. UserData ud = new UserData(data.getUsername(), data.getPassword(), new GregorianCalendar(TimeZone.getTimeZone("GMT")),
  518. adminname, data.getExpires(), null, 0, data.getRealName(), data.getEmail(), data.getEmailSecond(), data.getHomepage(),
  519. data.getReason(), data.getNotes(), data.getState());
  520. setUserData(ud, null, adminname, false);
  521. }
  522. if (data.getMailSubject() != null && data.getMailSubject().length() != 0) {
  523. // inform user about acception or dismiss
  524. sendMail(data.getEmail(), data.getMailSubject(), data.getMailMessage());
  525. }
  526. // Remove request from request list
  527. requestAdmin.removeRequest(data.getOldUsername());
  528. return true;
  529. }
  530. /**
  531. * Called by the server page. - If everything but username is null, it
  532. * returns, whether the username is OK (true) or it is already used (false).
  533. * - If everything but password is null and password is the email validation
  534. * code sent to the user, the request becomes available to the AdminTool and
  535. * an email is sent to the administrator, if the code is wrong, false is
  536. * returned. - If username, password, realname, email and reason is not
  537. * null, it saves the data and sends a validation email to the user. -
  538. * Otherwise an exception is thrown.
  539. */
  540. public synchronized boolean requestAccount(RequestData data) throws JPlagException {
  541. if (data.getRealName() == null && data.getEmail() == null && data.getEmailSecond() == null && data.getHomepage() == null
  542. && data.getReason() == null && data.getNotes() == null) {
  543. if (data.getPassword() == null && data.getUsername() != null) {
  544. if (exists(data.getUsername()))
  545. return false;
  546. else
  547. return !requestAdmin.exists(data.getUsername());
  548. } else if (data.getPassword() != null && data.getUsername() == null) {
  549. String username = requestAdmin.validateRequest(data.getPassword());
  550. RequestData rd = requestAdmin.getRequestData(username);
  551. // Send notification messages to all administrators with
  552. // notification to make them notice the new validated request
  553. NodeList userList = rootElement.getElementsByTagName("user");
  554. for (int i = 0; i < userList.getLength(); i++) {
  555. Element elem = (Element) userList.item(i);
  556. if ((parseInt(elem, "state") & MASK_REQUESTNOTIFY) != 0) {
  557. sendMail(elem.getAttribute("email"), MailTemplateAdmin.SERVER_REQUESTNOTIFY, rd);
  558. }
  559. }
  560. return true;
  561. }
  562. }
  563. if (data.getUsername() == null || data.getPassword() == null || data.getRealName() == null || data.getEmail() == null
  564. || data.getReason() == null)
  565. throw new JPlagException("requestAccount", "Username, password, " + "real name, primary email or reason is missing!",
  566. "Check your input!");
  567. if (!isValidUsername(data.getUsername()))
  568. throw new JPlagException("requestAccount", "Illegal new username!", "The username may only consist of the "
  569. + "following characters: 'A'-'Z', 'a'-'z', " + "'0'-'9', '@' and '.'");
  570. if (exists(data.getUsername()) || requestAdmin.exists(data.getUsername()))
  571. throw new JPlagException("requestAccount", "Username already used!", "Seems like somebody else was faster ;)");
  572. // Everything's fine, so add request to unverificated request list
  573. // and send a verification mail
  574. String vericode = requestAdmin.addRequest(data);
  575. sendMail(data.getEmail(), MailTemplateAdmin.SERVER_VERIFICATION, data, "code", vericode);
  576. return true;
  577. }
  578. public void extendAccount(String extendCode) throws JPlagException {
  579. if (extendCode.length() >= 12) // username must be at least one character
  580. {
  581. String username = extendCode.substring(11);
  582. System.out.println("[" + new Date() + "] extendAccount: username=" + username + " code=" + extendCode);
  583. Element userElem = getUserXMLElement(username);
  584. if (userElem != null) {
  585. Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
  586. if (userElem.getAttribute("askExtendCode").equals(extendCode)) {
  587. cal.add(Calendar.YEAR, 1);
  588. setNillableCalendar(userElem, "expires", cal);
  589. userElem.removeAttribute("expWarned");
  590. userElem.removeAttribute("askExtendCode");
  591. return;
  592. } else {
  593. Calendar expires = parseNillableCalendar(userElem, "expires");
  594. if (expires != null) {
  595. Calendar next2Week = (Calendar) cal.clone();
  596. next2Week.add(Calendar.DATE, 14);
  597. if (expires.after(next2Week)) {
  598. throw new JPlagException("extendAccount", "Your account has already been extended!",
  599. "You probably opened this page more than once and missed the result of the first time.");
  600. }
  601. }
  602. }
  603. }
  604. }
  605. throw new JPlagException("extendAccount", "Wrong extend code given: " + extendCode,
  606. "Please check the correct spelling of the code!");
  607. }
  608. /**
  609. * Sends a specified email to all developers
  610. */
  611. public void notifyDevelopers(String subject, String message) throws JPlagException {
  612. NodeList userList = rootElement.getElementsByTagName("user");
  613. for (int i = 0; i < userList.getLength(); i++) {
  614. Element elem = (Element) userList.item(i);
  615. if ((parseInt(elem, "state") & MASK_DEVELOPER) != 0)
  616. sendMail(elem.getAttribute("email"), subject, message);
  617. }
  618. }
  619. /**
  620. * Sets the developer flag for the specified user according to developer
  621. */
  622. public synchronized void setDeveloperState(String username, boolean developer) throws JPlagException {
  623. Element user = getUserXMLElement(username);
  624. if (user == null)
  625. throw new JPlagException("setDeveloperState", "User doesn't exist (anymore?)!", "Please update the user list!");
  626. int state = parseInt(user, "state");
  627. if (developer) {
  628. if ((state & MASK_DEVELOPER) != 0)
  629. throw new JPlagException("setDeveloperState", "You are already marked as a developer!", "Nothing changed.");
  630. state |= MASK_DEVELOPER;
  631. } else {
  632. if ((state & MASK_DEVELOPER) == 0)
  633. throw new JPlagException("setDeveloperState", "You are not marked as a developer!", "Nothing changed.");
  634. state &= ~MASK_DEVELOPER;
  635. }
  636. user.setAttribute("state", state + "");
  637. synchronized (userPassListMutex) {
  638. PassAndState pas = (PassAndState) userPassList.get(username);
  639. pas.state = state;
  640. }
  641. writeXMLFile();
  642. }
  643. public void stopUserAdmin() {
  644. doStopUserAdmin = true;
  645. }
  646. /**
  647. *
  648. * @param elem
  649. * User element to be checked
  650. * @param cal
  651. * Current date as calendar
  652. * @param next2Week
  653. * Date after which a expiration notice should be sent
  654. */
  655. private void checkExpired(Element elem, Calendar cal, Calendar next2Week) {
  656. Calendar expires = parseNillableCalendar(elem, "expires");
  657. if (expires != null) {
  658. int state = parseInt(elem, "state");
  659. if ((state & (MASK_EXPIRED | MASK_DEACTIVATED)) == 0) {
  660. if (cal.after(expires)) {
  661. System.out.println("[" + new Date() + "] User " + elem.getAttribute("username") + " expired (expires="
  662. + elem.getAttribute("expires") + ")");
  663. elem.setAttribute("state", (state | MASK_EXPIRED) + "");
  664. synchronized (userPassListMutex) {
  665. PassAndState pas = (PassAndState) userPassList.get(elem.getAttribute("username"));
  666. pas.state = state | MASK_EXPIRED;
  667. }
  668. try {
  669. sendMail(elem.getAttribute("email"), MailTemplateAdmin.SERVER_EXPIRED, elem);
  670. } catch (JPlagException ex) {
  671. System.out.println("Unable to send \"expired\" mail (" + ex.getExceptionType() + ": " + ex.getDescription() + "\n"
  672. + ex.getRepair());
  673. ex.printStackTrace();
  674. }
  675. } else if (next2Week.after(expires)) {
  676. if (!elem.hasAttribute("expWarned")) {
  677. if ((state & MASK_NOAUTOASKEXTEND) == 0 && parseInt(elem, "numOfSubs") != 0) {
  678. System.out.println("[" + new Date() + "] User " + elem.getAttribute("username")
  679. + " expires within two weeks! Asking for extend.");
  680. try {
  681. String extendCode = RequestAdmin.getRandomCode() + elem.getAttribute("username");
  682. sendMail(elem.getAttribute("email"), MailTemplateAdmin.SERVER_ASKEXTEND, elem, "code", extendCode);
  683. elem.setAttribute("expWarned", "");
  684. elem.setAttribute("askExtendCode", extendCode);
  685. } catch (JPlagException ex) {
  686. System.out.println("Unable to send \"askExtend" + "\" mail (" + ex.getExceptionType() + ": "
  687. + ex.getDescription() + "\n" + ex.getRepair());
  688. ex.printStackTrace();
  689. }
  690. } else {
  691. System.out.println("[" + new Date() + "] User " + elem.getAttribute("username") + " expires within two weeks!");
  692. try {
  693. sendMail(elem.getAttribute("email"), MailTemplateAdmin.SERVER_WARNEXPIRE, elem);
  694. elem.setAttribute("expWarned", "");
  695. } catch (JPlagException ex) {
  696. System.out.println("Unable to send \"warnExpire" + "\" mail (" + ex.getExceptionType() + ": "
  697. + ex.getDescription() + "\n" + ex.getRepair());
  698. ex.printStackTrace();
  699. }
  700. }
  701. }
  702. return;
  703. }
  704. }
  705. }
  706. elem.removeAttribute("expWarned");
  707. elem.removeAttribute("askExtendCode");
  708. }
  709. private void checkExpired(Element elem) {
  710. Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
  711. Calendar next2Week = (Calendar) cal.clone();
  712. next2Week.add(Calendar.DATE, 14);
  713. checkExpired(elem, cal, next2Week);
  714. }
  715. public void run() {
  716. System.out.println("[" + new Date() + "] UserAdministration started");
  717. try {
  718. while (!doStopUserAdmin) {
  719. // remove expired users
  720. Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
  721. Calendar next2Week = (Calendar) cal.clone();
  722. next2Week.add(Calendar.DATE, 14);
  723. synchronized (this) {
  724. NodeList userList = rootElement.getElementsByTagName("user");
  725. for (int i = userList.getLength() - 1; i >= 0; i--) {
  726. Element elem = (Element) userList.item(i);
  727. checkExpired(elem, cal, next2Week);
  728. }
  729. writeXMLFile();
  730. }
  731. // remove expired requests
  732. requestAdmin.removeExpiredUnvalidatedRequests();
  733. // get new current time
  734. cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
  735. long nowMillis = cal.getTimeInMillis();
  736. try {
  737. // wait until next midnight
  738. long sleepTime = ONEDAY - (nowMillis + ONEDAY - 1) % ONEDAY;
  739. String sleepTimeStr = ((sleepTime / 3600000 > 0) ? (sleepTime / 3600000) + " h " : "")
  740. + ((sleepTime / 60000 > 0) ? ((sleepTime / 60000) % 60) + " min " : "") + (sleepTime / 1000 % 60) + " sec";
  741. System.out.println("[" + new Date() + "] UserAdmin: I'll " + "sleep for " + sleepTimeStr);
  742. Thread.sleep(sleepTime);
  743. } catch (InterruptedException ex) {
  744. }
  745. }
  746. } catch (Exception ex) {
  747. ex.printStackTrace();
  748. }
  749. synchronized (this) {
  750. logWriter.close();
  751. mailLogWriter.close();
  752. }
  753. System.out.println("[" + new Date() + "] UserAdministration stopped!");
  754. }
  755. private class MailAuthenticator extends Authenticator {
  756. String username, password;
  757. public MailAuthenticator(String username, String password) {
  758. super();
  759. this.password = password;
  760. this.username = username;
  761. }
  762. public PasswordAuthentication getPasswordAuthentication() {
  763. return new PasswordAuthentication(this.username, password);
  764. }
  765. }
  766. private MailTemplate evalMailTemplate(String name, MailTemplateData data) {
  767. MailTemplateAdmin mailTemplateAdmin = JPlagCentral.getInstance().getMailTemplateAdmin();
  768. MailTemplate template = mailTemplateAdmin.getMailTemplate(MailTemplateAdmin.MAIL_SERVER, name);
  769. if (template == null)
  770. return null;
  771. // Evaluate subject
  772. template.setSubject(mailTemplateAdmin.evalTemplateString(template.getSubject(), data));
  773. // Evaluate message body
  774. template.setData(mailTemplateAdmin.evalTemplateString(template.getData(), data));
  775. return template;
  776. }
  777. private void sendMail(String destMail, String name, MailTemplateData data) throws JPlagException {
  778. MailTemplate mail = evalMailTemplate(name, data);
  779. if (mail == null) {
  780. System.out.println("UserAdmin.sendMail(): Unable to get \"" + name + "\" message!!");
  781. throw new JPlagException("UserAdminException", "Internal server error: Unable to find mail template!",
  782. "Please contact the JPlag admin!");
  783. } else
  784. sendMail(destMail, mail.getSubject(), mail.getData());
  785. }
  786. private void sendMail(String destMail, String name, RequestData data, String paramname, String param) throws JPlagException {
  787. MailTemplateData tempData = new MailTemplateData(data);
  788. if (paramname != null)
  789. tempData.put(paramname, param);
  790. sendMail(destMail, name, tempData);
  791. }
  792. private void sendMail(String destMail, String name, RequestData data) throws JPlagException {
  793. sendMail(destMail, name, new MailTemplateData(data));
  794. }
  795. private void sendMail(String destMail, String name, Element elem, String paramname, String param) throws JPlagException {
  796. MailTemplateData tempData = new MailTemplateData(elem);
  797. if (paramname != null)
  798. tempData.put(paramname, param);
  799. sendMail(destMail, name, tempData);
  800. }
  801. private void sendMail(String destMail, String name, Element elem) throws JPlagException {
  802. sendMail(destMail, name, new MailTemplateData(elem));
  803. }
  804. public void sendMail(String destMail, String subject, String message) throws JPlagException {
  805. Properties mailProps = PropertiesLoader.loadProps("jplagWebService/serverAccess/mailconfig.properties");
  806. // Gets the System properties
  807. Properties l_props = System.getProperties();
  808. // Puts the SMTP server name to properties object
  809. l_props.put("mail.smtp.host", mailProps.getProperty("mail.smtp.host", "smtp.ira.uni-karlsruhe.de"));
  810. l_props.put("mail.smtp.auth", mailProps.getProperty("mail.smtp.auth", "true"));
  811. l_props.put("mail.smtp.starttls.enable", mailProps.getProperty("mail.smtp.starttls.enable", "true"));
  812. l_props.put("mail.SSLSocketFactory.class",
  813. mailProps.getProperty("mail.SSLSocketFactory.class", "jplagWebService.serverAccess.TrustAllSSLSocketFactory"));
  814. // Get the default Session using Properties Object
  815. Session session = Session.getInstance(l_props, new MailAuthenticator(
  816. mailProps.getProperty("mail.smtp.auth.user", "jplag"),
  817. mailProps.getProperty("mail.smtp.auth.pass", "zentis!?")));
  818. //session.setDebug(true); // Enable the debug mode
  819. try {
  820. MimeMessage msg = new MimeMessage(session); // Create a New message
  821. msg.setFrom(new InternetAddress(mailProps.getProperty("from_address", "smtp.ira.uni-karlsruhe.de")));
  822. msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(destMail, false));
  823. msg.setSubject(subject, "UTF-8");
  824. msg.setContent(message, "text/plain; charset=UTF-8");
  825. msg.setSentDate(new Date());
  826. // Send the message
  827. Transport.send(msg);
  828. // If here, then message is successfully sent.
  829. } catch (Exception e) {
  830. System.out.println("Exception in sendMail: destMail=" + destMail);
  831. e.printStackTrace();
  832. throw new JPlagException("sendMail", "Exception caught: " + e.toString(), "Does this help?");
  833. }
  834. synchronized (this) {
  835. if (mailLogWriter != null)
  836. mailLogWriter.println("[" + new Date() + "] Sent the following " + "mail to " + destMail + ":\n\nSubject: " + subject
  837. + "\n\nMessage: " + message);
  838. }
  839. }
  840. }