Index: 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteClient.java =================================================================== diff -u --- 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteClient.java (revision 0) +++ 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteClient.java (revision d1558f68d1dbddfdfdc8d301d831d025c8a916d7) @@ -0,0 +1,915 @@ +package net.gjerull.etherpad.client; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * A client for talking to Etherpad Lite's HTTP JSON API.
+ *
+ * Example:
+ *
+ * + * EPLiteClient api = new EPLiteClient("http://etherpad.mysite.com", "FJ7jksalksdfj83jsdflkj");
+ * Map pad = api.getText("my_pad");
+ * String pad = pad.get("text").toString(); + *
+ */ +public class EPLiteClient { + private static final String DEFAULT_API_VERSION = "1.2.13"; + private static final String DEFAULT_ENCODING = "UTF-8"; + private final EPLiteConnection connection; + + /** + * Initializes a new net.gjerull.etherpad.client.EPLiteClient object. + * The default Etherpad Lite API version (in DEFAULT_API_VERSION) will be used. + * + * @param url an absolute url, including protocol, to the EPL api + * @param apiKey the API Key + */ + public EPLiteClient(String url, String apiKey) { + this.connection = new EPLiteConnection(url, apiKey, DEFAULT_API_VERSION, DEFAULT_ENCODING); + } + + /** + * Initializes a new net.gjerull.etherpad.client.EPLiteClient object. + * The specified Etherpad Lite API version will be used. + * + * @param url an absolute url, including protocol, to the EPL api + * @param apiKey the API Key + * @param apiVersion the API version + */ + public EPLiteClient(String url, String apiKey, String apiVersion, String encoding) { + this.connection = new EPLiteConnection(url, apiKey, apiVersion, encoding); + } + + // Groups + // Pads may belong to a group. These pads are not considered "public", and won't be available through the Web UI without a session. + + /** + * Creates a new Group. The group id is returned in "groupID" in the Map. + * + * @return Map with groupID + */ + public Map createGroup() { + return this.connection.post("createGroup"); + } + + /** + * Creates a new Group for groupMapper if one doesn't already exist. Helps you map your application's groups to Etherpad Lite's groups. + * The group id is returned in "groupID" in the Map. + * + * @param groupMapper your group mapper string + * @return Map with groupID + */ + public Map createGroupIfNotExistsFor(String groupMapper) { + Map args = new HashMap<>(); + args.put("groupMapper", groupMapper); + return this.connection.post("createGroupIfNotExistsFor", args); + } + + /** + * Delete group. + * + * @param groupID string + */ + public void deleteGroup(String groupID) { + Map args = new HashMap<>(); + args.put("groupID", groupID); + this.connection.post("deleteGroup", args); + } + + /** + * List all the padIDs in a group. They will be in an array inside "padIDs". + * + * @param groupID string + * @return Map + */ + public Map listPads(String groupID) { + Map args = new HashMap<>(); + args.put("groupID", groupID); + return this.connection.get("listPads", args); + } + + /** + * Create a pad in this group. + * + * @param groupID the group the pad belongs to + * @param padName name of the pad + */ + public Map createGroupPad(String groupID, String padName) { + Map args = new HashMap<>(); + args.put("groupID", groupID); + args.put("padName", padName); + return this.connection.post("createGroupPad", args); + } + + /** + * Create a pad in this group, with initial text. + * + * @param groupID the group the pad belongs to + * @param padName name of the pad + * @param text Initial text in the pad + */ + public Map createGroupPad(String groupID, String padName, String text) { + Map args = new HashMap<>(); + args.put("groupID", groupID); + args.put("padName", padName); + args.put("text", text); + return this.connection.post("createGroupPad", args); + } + + /** + * Lists all existing groups. The group ids are returned in "groupIDs". + * + * @return Map with list of groupIDs + */ + public Map listAllGroups() { + return this.connection.get("listAllGroups"); + } + + // Authors + // These authors are bound to the attributes the users choose (color and name). + // The author id is returned in "authorID". + + /** + * Create a new author. + * + * @return Map with authorID + */ + public Map createAuthor() { + return this.connection.get("createAuthor"); + } + + /** + * Create a new author with the given name. The author id is returned in "authorID". + * + * @param name string + * @return Map with authorID + */ + public Map createAuthor(String name) { + Map args = new HashMap<>(); + args.put("name", name); + return this.connection.post("createAuthor", args); + } + + /** + * Creates a new Author for authorMapper if one doesn't already exist. + * Helps you map your application's authors to Etherpad Lite's authors. + * The author id is returned in "authorID". + * + * @param authorMapper string + * @return Map with authorID + */ + public Map createAuthorIfNotExistsFor(String authorMapper) { + Map args = new HashMap<>(); + args.put("authorMapper", authorMapper); + return this.connection.post("createAuthorIfNotExistsFor", args); + } + + /** + * Creates a new Author for authorMapper if one doesn't already exist. + * Helps you map your application's authors to Etherpad Lite's authors. + * The author id is returned in "authorID". + * + * @param authorMapper string + * @param name string + * @return Map with authorID + */ + public Map createAuthorIfNotExistsFor(String authorMapper, String name) { + Map args = new HashMap<>(); + args.put("authorMapper", authorMapper); + args.put("name", name); + return this.connection.post("createAuthorIfNotExistsFor", args); + } + + /** + * List the ids of pads the author has edited. They will be in an array inside "padIDs". + * + * @param authorId the authors's id string + * @return Map + */ + public Map listPadsOfAuthor(String authorId) { + Map args = new HashMap<>(); + args.put("authorID", authorId); + return this.connection.get("listPadsOfAuthor", args); + } + + /** + * Returns the Author Name of the author. + * + * @param authorId the author's id string + * @return authorName + */ + public String getAuthorName(String authorId) { + Map args = new HashMap<>(); + args.put("authorID", authorId); + return (String) this.connection.getObject("getAuthorName", args); + } + + // Sessions + // Sessions can be created between a group and an author. This allows an author to access more than one group. The sessionID will be set as a + // cookie to the client and is valid until a certain date. Only users with a valid session for this group, can access group pads. You can create a + // session after you authenticated the user at your web application, to give them access to the pads. You should save the sessionID of this session + // and delete it after the user logged out. + + /** + * Create a new session for the given author in the given group, valid until the given UNIX time. + *
+ * Example:
+ *
+ * + * import java.util.Date;
+ * ...
+ * Date now = new Date();
+ * long in1Hour = (now.getTime() + (60L * 60L * 1000L) / 1000L);
+ * String sessID1 = api.createSession(groupID, authorID, in1Hour); + *
+ * + * @param groupID string + * @param authorID string + * @param validUntil long UNIX timestamp in seconds + * @return Map with sessionID + */ + public Map createSession(String groupID, String authorID, long validUntil) { + Map args = new HashMap<>(); + args.put("groupID", groupID); + args.put("authorID", authorID); + args.put("validUntil", String.valueOf(validUntil)); + return this.connection.post("createSession", args); + } + + /** + * Create a new session for the given author in the given group valid for the given number of hours. + *
+ * Example:
+ *
+ * + * // in 2 hours
+ * String sessID1 = api.createSession(groupID, authorID, 2); + *
+ * + * @param groupID string + * @param authorID string + * @param sessionDuration int duration of session in hours + * @return Map with sessionID + */ + public Map createSession(String groupID, String authorID, int sessionDuration) { + long inNHours = ((new Date()).getTime() + ((long)sessionDuration * 60L * 60L * 1000L)) / 1000L; + return this.createSession(groupID, authorID, inNHours); + } + + /** + * Create a new session for the given author in the given group, valid until the given datetime. + *
+ * Example:
+ *
+ * + * import java.util.Date;
+ * import java.text.DateFormat;
+ * import java.text.SimpleDateFormat;
+ * import java.util.TimeZone;
+ * ...
+ * DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ * dfm.setTimeZone(TimeZone.getTimeZone("GMT-5"));
+ * Date longTime = dfm.parse("2056-01-15 20:15:00");
+ * String sessID = api.createSession(groupID, authorID, longTime); + *
+ * + * @param groupID string + * @param authorID string + * @param validUntil Date + * @return Map with sessionID + */ + public Map createSession(String groupID, String authorID, Date validUntil) { + long seconds = validUntil.getTime() / 1000L; + return this.createSession(groupID, authorID, seconds); + } + + /** + * Delete a session. + * + * @param sessionID string + */ + public void deleteSession(String sessionID) { + Map args = new HashMap<>(); + args.put("sessionID", sessionID); + this.connection.post("deleteSession", args); + } + + /** + * Returns information about a session: authorID, groupID and validUntil. + * + * @param sessionID string + * @return Map + */ + public Map getSessionInfo(String sessionID) { + Map args = new HashMap<>(); + args.put("sessionID", sessionID); + return this.connection.get("getSessionInfo", args); + } + + /** + * List all the sessions IDs in a group. Returned as a Map of sessionIDs keys, with values of Maps containing + * groupID, authorID, and validUntil. + * + * @param groupID string + * @return Map + */ + public Map listSessionsOfGroup(String groupID) { + Map args = new HashMap<>(); + args.put("groupID", groupID); + return this.connection.get("listSessionsOfGroup", args); + } + + /** + * List all the sessions IDs belonging to an author. Returned as a Map of sessionIDs keys, with values of Maps containing + * groupID, authorID, and validUntil. + * + * @param authorID string + * @return Map + */ + public Map listSessionsOfAuthor(String authorID) { + Map args = new HashMap<>(); + args.put("authorID", authorID); + return this.connection.get("listSessionsOfAuthor", args); + } + + // Pad content + + /** + * Returns a Map containing the latest revision of the pad's text. + * The text is stored under "text". + * + * @param padId the pad's id string + * @return a Map with the text content of pad + */ + public Map getText(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getText", args); + } + + /** + * Returns a Map containing the a specific revision of the pad's text. + * The text is stored under "text". + * + * @param padId the pad's id string + * @param rev the revision number + * @return a Map with the text content of pad in given revision + */ + public Map getText(String padId, long rev) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("rev", rev); + return this.connection.get("getText", args); + } + + /** + * Creates a new revision with the given text. + * + * @param padId the pad's id string + * @param text the pad's new text + */ + public void setText(String padId, String text) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("text", text); + this.connection.post("setText", args); + } + + /** + * Creates a new revision with the given text appended to the existing text. + * + * API >= 1.2.13 + * + * @param padId the pad's id string + * @param text the pad's new text + */ + public void appendText(String padId, String text) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("text", text); + this.connection.post("appendText", args); + } + + /** + * Returns a Map containing the current revision of the pad's text as HTML. + * The html is stored under "html". + * + * @param padId the pad's id string + * @return a Map with the HTML content of pad + */ + public Map getHTML(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getHTML", args); + } + + /** + * Returns a Map containing the a specific revision of the pad's text as HTML. + * The html is stored under "html". + * + * @param padId the pad's id string + * @param rev the revision number + * @return a Map with the HTML content of pad in given revision + */ + public Map getHTML(String padId, long rev) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("rev", rev); + return this.connection.get("getHTML", args); + } + + /** + * Creates a new revision with the given html. + * + * @param padId the pad's id string + * @param html the pad's new html text + */ + public void setHTML(String padId, String html) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("html", html); + this.connection.post("setHTML", args); + } + + /** + * Returns the attribute pool of a pad + * + * API >= 1.2.8 + * + * @param padId the pad's id string + * @return a Map with the attribute pool of a pad + */ + public Map getAttributePool(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getAttributePool", args); + } + + /** + * Get the changeset at the last revision. + * + * API >= 1.2.8 + * + * @param padId the pad's id string + * @return the changeset at the last revision. + */ + public String getRevisionChangeset(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return (String) this.connection.getObject("getRevisionChangeset", args); + } + + /** + * Get the changeset at a given revision. + * + * API >= 1.2.8 + * + * @param padId the pad's id string + * @param rev the revision number + * @return the changeset at a given revision. + */ + public String getRevisionChangeset(String padId, long rev) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("rev", rev); + return (String) this.connection.getObject("getRevisionChangeset", args); + } + + /** + * Returns an object of diffs from 2 points in a pad + * + * API >= 1.2.7 + * + * @param padId the pad's id string + * @param startRev the start revision number + * @param endRev the end revision number + * @return a Map of diffs from 2 points in a pad + */ + public Map createDiffHTML(String padId, long startRev, long endRev) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("startRev", startRev); + args.put("endRev", endRev); + return this.connection.get("createDiffHTML", args); + } + + // Chat + + /** + * Returns the complete chat history of pad + * + * API >= 1.2.7 + * + * @param padId the pad's id string + * @return the whole chat histroy + */ + public Map getChatHistory(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getChatHistory", args); + } + + /** + * Returns the chat history of pad with index between start and end + * + * API >= 1.2.7 + * + * @param padId the pad's id string + * @param start the start index + * @param end the end index + * @return a part of the chat history, between start and end + */ + public Map getChatHistory(String padId, long start, long end) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("start", start); + args.put("end", end); + return this.connection.get("getChatHistory", args); + } + + /** + * Returns the chatHead (last number of the last chat-message) of the pad + * + * API >= 1.2.7 + * + * @param padId the pad's id string + * @return the last number of the last chat-message + */ + public Map getChatHead(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getChatHead", args); + } + + /** + * Creates a chat message, saves it to the database and sends it to all + * connected clients of this pad, using the current time as timestamp. + * + * API >= 1.2.12 + * + * @param padId the pad's id string + * @param text the text of this chat entry + * @param authorId the author of this chat entry + */ + public Map appendChatMessage(String padId, String text, String authorId) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("text", text); + args.put("authorID", authorId); + return this.connection.post("appendChatMessage", args); + } + + /** + * Creates a chat message, saves it to the database and sends it to all + * connected clients of this pad. + * + * API >= 1.2.12 + * + * @param padId the pad's id string + * @param text the text of this chat entry + * @param authorId the author of this chat entry + * @param time the timestamp of this chat entry + */ + public Map appendChatMessage(String padId, String text, String authorId, long time) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("text", text); + args.put("authorID", authorId); + args.put("time", time); + return this.connection.post("appendChatMessage", args); + } + + // Pads + // Group pads are normal pads, but with the name schema GROUPID$PADNAME. A security manager controls access of them and its + // forbidden for normal pads to include a $ in the name. + + /** + * Returns a list of all pads. + * + * @return a Map with list of pad id's + */ + public Map listAllPads() { + return this.connection.get("listAllPads"); + } + + /** + * Create a new pad. + * + * @param padId the pad's id string + */ + public void createPad(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + this.connection.post("createPad", args); + } + + /** + * Create a new pad with the given initial text. + * + * @param padId the pad's id string + * @param text the initial text string + */ + public void createPad(String padId, String text) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("text", text); + this.connection.post("createPad", args); + } + + /** + * Returns the number of revisions of this pad. The number is in "revisions". + * + * @param padId the pad's id string + * @return a Map with the number of revisions + */ + public Map getRevisionsCount(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getRevisionsCount", args); + } + + /** + * Returns the number of saved revisions of this pad + * + * API >= 1.2.11 + * + * @param padId the pad's id string + * @return a Map with number of saved revisions + */ + public Map getSavedRevisionsCount(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getSavedRevisionsCount", args); + } + + /** + * returns the list of saved revisions of this pad + * + * API >= 1.2.11 + * + * @param padId the pad's id string + * @return a Map with the list of saved revision numbers + */ + public Map listSavedRevisions(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("listSavedRevisions", args); + } + + /** + * Saves the latest revision + * + * API >= 1.2.11 + * + * @param padId the pad's id string + */ + public void saveRevision(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + this.connection.post("saveRevision", args); + } + + /** + * Saves the given revision + * + * API >= 1.2.11 + * + * @param padId the pad's id string + * @param rev the revision to be saved + */ + public void saveRevision(String padId, long rev) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("rev", rev); + this.connection.post("saveRevision", args); + } + + /** + * Get the number of users currently editing a pad. + * + * @param padId the pad's id string + * @return a Map with the padUsersCount + */ + public Map padUsersCount(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("padUsersCount", args); + } + + /** + * Returns the list of users that are currently editing this pad. + * A padUser has the values: "colorId", "name" and "timestamp". + * + * @param padId the pad's id string + * @return a Map with a List of pad user maps + */ + public Map padUsers(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("padUsers", args); + } + + /** + * Deletes a pad. + * + * @param padId the pad's id string + */ + public void deletePad(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + this.connection.post("deletePad", args); + } + + /** + * Copies a pad with full history and chat. If the destination exists the copy will fail. + * + * API >= 1.2.8 + * + * @param sourcePadId the id of the source pad + * @param destinationPadId the id of the destination pad + */ + public void copyPad(String sourcePadId, String destinationPadId) { + copyPad(sourcePadId, destinationPadId, false); + } + + /** + * Copies a pad with full history and chat. + * + * API >= 1.2.8 + * + * @param sourcePadId the id of the source pad + * @param destinationPadId the id of the destination pad + * @param force if force is true and the destination pad exists, it will be overwritten. + */ + public void copyPad(String sourcePadId, String destinationPadId, boolean force) { + Map args = new HashMap<>(); + args.put("sourceID", sourcePadId); + args.put("destinationID", destinationPadId); + args.put("force", force); + this.connection.post("copyPad", args); + } + + /** + * Moves a pad. If the destination exists the copy will fail. + * + * API >= 1.2.8 + * + * @param sourcePadId the id of the source pad + * @param destinationPadId the id of the destination pad + */ + public void movePad(String sourcePadId, String destinationPadId) { + copyPad(sourcePadId, destinationPadId, false); + } + + /** + * Moves a pad. + * + * API >= 1.2.8 + * + * @param sourcePadId the id of the source pad + * @param destinationPadId the id of the destination pad + * @param force if force is true and the destination pad exists, it will be overwritten. + */ + public void movePad(String sourcePadId, String destinationPadId, boolean force) { + Map args = new HashMap<>(); + args.put("sourceID", sourcePadId); + args.put("destinationID", destinationPadId); + args.put("force", force); + this.connection.post("movePad", args); + } + + /** + * Get the pad's read-only id. + * + * @param padId the pad's id string + * @return a Map with the readOnlyID + */ + public Map getReadOnlyID(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getReadOnlyID", args); + } + + /** + * Get the pad's id from the read only id + * + * API >= 1.2.10 + * + * @param readOnlyPadId the pad's read only id string + * @return a Map with the padID + */ + public Map getPadID(String readOnlyPadId) { + Map args = new HashMap<>(); + args.put("roID", readOnlyPadId); + return this.connection.get("getPadID", args); + } + + /** + * Sets the pad's public status. + * This is only applicable to group pads. + * + * @param padId the pad's id string + * @param publicStatus boolean + */ + public void setPublicStatus(String padId, Boolean publicStatus) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("publicStatus", publicStatus); + this.connection.post("setPublicStatus", args); + } + + /** + * Gets the pad's public status. + * + * @param padId the pad's id string + * @return a Map with the Boolean publicStatus + */ + public Map getPublicStatus(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getPublicStatus", args); + } + + /** + * Sets the pad's password. This is only applicable to group pads. + * + * @param padId the pad's id string + * @param password string + */ + public void setPassword(String padId, String password) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("password", password); + this.connection.post("setPassword", args); + } + + /** + * Checks whether the pad is password-protected or not. + * + * @param padId the pad's id string + * @return a Map with the Boolean passwordProtection + */ + public Map isPasswordProtected(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("isPasswordProtected", args); + } + + /** + * List the ids of authors who have edited a pad. + * + * @param padId the pad's id string + * @return a Map with a List of author ids + */ + public Map listAuthorsOfPad(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("listAuthorsOfPad", args); + } + + /** + * Get the pad's last edit date. + * + * @param padId the pad's id string + * @return a Map with lastEdited timestamp. + */ + public Map getLastEdited(String padId) { + Map args = new HashMap<>(); + args.put("padID", padId); + return this.connection.get("getLastEdited", args); + } + + /** + * Sends a custom message of type msg to the pad. + * + * @param padId the pad's id string + * @param msg the message to send + */ + public void sendClientsMessage(String padId, String msg) { + Map args = new HashMap<>(); + args.put("padID", padId); + args.put("msg", msg); + this.connection.post("sendClientsMessage", args); + } + + /** + * Runs without error if current api token is valid + * + * API >= 1.2 + */ + public void checkToken() { + this.connection.get("checkToken"); + } + + /** + * Returns true if the connection is using SSL/TLS, false if not. + * + * @return boolean + */ + public boolean isSecure() { + return (this.connection.uri.getPort() == 443); + } +} Index: 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteConnection.java =================================================================== diff -u --- 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteConnection.java (revision 0) +++ 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteConnection.java (revision d1558f68d1dbddfdfdc8d301d831d025c8a916d7) @@ -0,0 +1,317 @@ +package net.gjerull.etherpad.client; + +import java.io.UnsupportedEncodingException; +import java.net.*; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.HashMap; +import java.util.Iterator; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +/** + * Connection object for talking to and parsing responses from the Etherpad Lite Server. + */ +public class EPLiteConnection { + public static final int CODE_OK = 0; + public static final int CODE_INVALID_PARAMETERS = 1; + public static final int CODE_INTERNAL_ERROR = 2; + public static final int CODE_INVALID_METHOD = 3; + public static final int CODE_INVALID_API_KEY = 4; + + /** + * The url of the API + */ + public final URI uri; + + /** + * The API key + */ + public final String apiKey; + + /** + * The Etherpad Lite API version + */ + public final String apiVersion; + + /** + * The character encoding of your application + */ + public final String encoding; + + /** + * Initializes a new net.gjerull.etherpad.client.EPLiteConnection object. + * + * @param url an absolute url, including protocol, to the EPL api + * @param apiKey the API Key + * @param apiVersion the API version + */ + public EPLiteConnection(String url, String apiKey, String apiVersion, String encoding) { + if (url.endsWith("/")) { + url = url.substring(0, url.length()-1); + } + this.uri = URI.create(url); + this.apiKey = apiKey; + this.apiVersion = apiVersion; + this.encoding = encoding; + } + + /** + * GETs from the HTTP JSON API. + * + * @param apiMethod the name of the API method to call + * @return Object + */ + public Object getObject(String apiMethod) { + return this.getObject(apiMethod, new HashMap()); + } + + /** + * GETs from the HTTP JSON API. + * + * @param apiMethod the name of the API method to call + * @return Map + */ + public Map get(String apiMethod) { + Map response = (Map) this.getObject(apiMethod); + return (response != null) ? response : new HashMap(); + } + + /** + * GETs from the HTTP JSON API. + * + * @param apiMethod the name of the API method to call + * @param apiArgs a HashMap of url/form parameters. apikey will be set automatically + * @return Object + */ + public Object getObject(String apiMethod, Map apiArgs) { + String path = this.apiPath(apiMethod); + String query = this.queryString(apiArgs, false); + URL url = apiUrl(path, query); + Request request = new GETRequest(url); + return this.call(request); + } + + /** + * GETs from the HTTP JSON API. + * + * @param apiMethod the name of the API method to call + * @param apiArgs a HashMap of url/form parameters. apikey will be set automatically + * @return Map + */ + public Map get(String apiMethod, Map apiArgs) { + Map response = (Map) this.getObject(apiMethod, apiArgs); + return (response != null) ? response : new HashMap(); + } + + /** + * POSTs to the HTTP JSON API. + * + * @param apiMethod the name of the API method to call + * @return Object + */ + public Object postObject(String apiMethod) { + return this.postObject(apiMethod, new HashMap()); + } + + /** + * POSTs to the HTTP JSON API. + * + * @param apiMethod the name of the API method to call + * @return Map + */ + public Map post(String apiMethod) { + Map response = (Map) this.postObject(apiMethod); + return (response != null) ? response : new HashMap(); + } + + /** + * POSTs to the HTTP JSON API. + * + * @param apiMethod the name of the API method to call + * @param apiArgs a HashMap of url/form parameters. apikey will be set automatically + * @return Object + */ + public Object postObject(String apiMethod, Map apiArgs) { + String path = this.apiPath(apiMethod); + String query = this.queryString(apiArgs, true); + URL url = apiUrl(path, null); + Request request = new POSTRequest(url, query); + return this.call(request); + } + + /** + * POSTs to the HTTP JSON API. + * + * @param apiMethod the name of the API method to call + * @param apiArgs a HashMap of url/form parameters. apikey will be set automatically + * @return Map + */ + public Map post(String apiMethod, Map apiArgs) { + Map response = (Map) this.postObject(apiMethod, apiArgs); + return (response != null) ? response : new HashMap(); + } + + /** + * Handle error condition and returns the parsed content + * + * @param jsonString a valid JSON string + * @return Object + */ + protected Object handleResponse(String jsonString) { + try { + JSONParser parser = new JSONParser(); + Map response = (Map) parser.parse(jsonString); + // Act on the response code + if (response.get("code") != null) { + int code = ((Long) response.get("code")).intValue(); + switch ( code ) { + // Valid code, parse the response + case CODE_OK: + return response.get("data"); + // Invalid code, throw an exception with the message + case CODE_INVALID_PARAMETERS: + case CODE_INTERNAL_ERROR: + case CODE_INVALID_METHOD: + case CODE_INVALID_API_KEY: + throw new EPLiteException((String)response.get("message")); + default: + throw new EPLiteException("An unknown error has occurred while handling the response: " + jsonString); + } + // No response code, something's really wrong + } else { + throw new EPLiteException("An unexpected response from the server: " + jsonString); + } + } catch (ParseException e) { + throw new EPLiteException("Unable to parse JSON response (" + jsonString + ")", e); + } + } + + /** + * Returns the URL for the api path and query. + * + * @param path the api path + * @param query the query string (may be null) + * @return URL + */ + protected URL apiUrl(String path, String query) { + try { + return new URL(new URI(this.uri.getScheme(), null, this.uri.getHost(), this.uri.getPort(), path, query, null).toString()); + } catch (MalformedURLException | URISyntaxException e) { + throw new EPLiteException("Error in the URL to the Etherpad Lite instance (" + e.getClass() + "): " + e.getMessage()); + } + } + + /** + * Returns a URI path for the API method + * + * @param apiMethod the api method + * @return String + */ + protected String apiPath(String apiMethod) { + return this.uri.getPath() + "/api/" + this.apiVersion + "/" + apiMethod; + } + + /** + * Returns a query string made from HashMap keys and values + * + * @param apiArgs the api arguments in a HashMap + * @return String + */ + protected String queryString(Map apiArgs, boolean urlEncode) { + StringBuilder strArgs = new StringBuilder(); + apiArgs.put("apikey", this.apiKey); + Iterator i = apiArgs.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + Object key = entry.getKey(); + Object value = entry.getValue(); + if (urlEncode) { + try { + if (key instanceof String) { + URLEncoder.encode((String) key, this.encoding); + } + if (value instanceof String) { + value = URLEncoder.encode((String) value, this.encoding); + } + } catch (UnsupportedEncodingException e) { + throw new EPLiteException(String.format( + "Unable to URLEncode using encoding '%s'", this.encoding), e); + } + } + strArgs.append(key).append("=").append(value); + if (i.hasNext()) { + strArgs.append("&"); + } + } + return strArgs.toString(); + } + + /** + * Calls the HTTP JSON API. + * + * @param request the request object to send + * @return HashMap + */ + private Object call(Request request) { + trustServerAndCertificate(); + + try { + String response = request.send(); + return this.handleResponse(response); + } + catch (EPLiteException e) { + throw e; + } + catch (Exception e) { + throw new EPLiteException("Unable to connect to Etherpad Lite instance (" + e.getClass() + "): " + e.getMessage()); + } + } + + /** + * Creates a trust manager to trust all certificates if you open a ssl connection + */ + private void trustServerAndCertificate() { + // Create a trust manager that does not validate certificate chains + TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, String authType) { + } + + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, String authType) { + } + } + }; + + // Install the all-trusting trust manager + try { + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new EPLiteException("Unable to create SSL context", e); + } + + HostnameVerifier hv = new HostnameVerifier() { + //@Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + HttpsURLConnection.setDefaultHostnameVerifier(hv); + } +} Index: 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteException.java =================================================================== diff -u --- 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteException.java (revision 0) +++ 3rdParty_sources/etherpad/net/gjerull/etherpad/client/EPLiteException.java (revision d1558f68d1dbddfdfdc8d301d831d025c8a916d7) @@ -0,0 +1,11 @@ +package net.gjerull.etherpad.client; + +public class EPLiteException extends RuntimeException { + public EPLiteException(String message) { + super(message); + } + + public EPLiteException(String message, Throwable cause) { + super(message, cause); + } +} Index: 3rdParty_sources/etherpad/net/gjerull/etherpad/client/GETRequest.java =================================================================== diff -u --- 3rdParty_sources/etherpad/net/gjerull/etherpad/client/GETRequest.java (revision 0) +++ 3rdParty_sources/etherpad/net/gjerull/etherpad/client/GETRequest.java (revision d1558f68d1dbddfdfdc8d301d831d025c8a916d7) @@ -0,0 +1,44 @@ +package net.gjerull.etherpad.client; + +import java.net.URL; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * A class for easily executing an HTTP GET request.
+ *
+ * Example:
+ *
+ * + * Request req = new GETRequest(url_object);
+ * String resp = req.send();
+ *
+ */ +public class GETRequest implements Request { + private final URL url; + + /** + * Instantiates a new GETRequest. + * + * @param url the URL object + */ + public GETRequest(URL url) { + this.url = url; + } + + /** + * Sends the request and returns the response. + * + * @return String + */ + public String send() throws Exception { + BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream())); + StringBuilder response = new StringBuilder(); + String buffer; + while ((buffer = in.readLine()) != null) { + response.append(buffer); + } + in.close(); + return response.toString(); + } +} Index: 3rdParty_sources/etherpad/net/gjerull/etherpad/client/POSTRequest.java =================================================================== diff -u --- 3rdParty_sources/etherpad/net/gjerull/etherpad/client/POSTRequest.java (revision 0) +++ 3rdParty_sources/etherpad/net/gjerull/etherpad/client/POSTRequest.java (revision d1558f68d1dbddfdfdc8d301d831d025c8a916d7) @@ -0,0 +1,56 @@ +package net.gjerull.etherpad.client; + +import java.net.URL; +import java.net.URLConnection; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; + +/** + * A class for easily executing an HTTP POST request.
+ *
+ * Example:
+ *
+ * + * Request req = new POSTRequest(url_object);
+ * String resp = req.send();
+ *
+ */ +public class POSTRequest implements Request { + private final URL url; + private final String body; + + /** + * Instantiates a new POSTRequest. + * + * @param url the URL object + * @param body url-encoded (application/x-www-form-urlencoded) request body + */ + public POSTRequest(URL url, String body) { + this.url = url; + this.body = body; + } + + /** + * Sends the request and returns the response. + * + * @return String + */ + public String send() throws Exception { + URLConnection con = this.url.openConnection(); + con.setDoOutput(true); + + OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream()); + out.write(this.body); + out.close(); + + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + StringBuilder response = new StringBuilder(); + String buffer; + while ((buffer = in.readLine()) != null) { + response.append(buffer); + } + in.close(); + return response.toString(); + } +} Index: 3rdParty_sources/etherpad/net/gjerull/etherpad/client/Request.java =================================================================== diff -u --- 3rdParty_sources/etherpad/net/gjerull/etherpad/client/Request.java (revision 0) +++ 3rdParty_sources/etherpad/net/gjerull/etherpad/client/Request.java (revision d1558f68d1dbddfdfdc8d301d831d025c8a916d7) @@ -0,0 +1,5 @@ +package net.gjerull.etherpad.client; + +public interface Request { + String send() throws Exception; +} Index: 3rdParty_sources/versions.txt =================================================================== diff -u -r2ab1076d8852ca61f6e633ea31de878497c3023a -rd1558f68d1dbddfdfdc8d301d831d025c8a916d7 --- 3rdParty_sources/versions.txt (.../versions.txt) (revision 2ab1076d8852ca61f6e633ea31de878497c3023a) +++ 3rdParty_sources/versions.txt (.../versions.txt) (revision d1558f68d1dbddfdfdc8d301d831d025c8a916d7) @@ -27,6 +27,8 @@ CSRF Guard master from 2020.01.07 with a custom modification in CsrfGuard.java +Etherpad Client 1.2.13 + Hibernate Core 5.3.6 httpunit 1.7