diff options
Diffstat (limited to '')
-rw-r--r-- | infrastructure/com.etherpad.openofficeservice/importexport.scala | 287 | ||||
-rw-r--r-- | infrastructure/com.etherpad/easysync2support.scala (renamed from trunk/infrastructure/com.etherpad/easysync2support.scala) | 0 | ||||
-rw-r--r-- | infrastructure/com.etherpad/licensing.scala | 169 | ||||
-rw-r--r-- | infrastructure/com.etherpad/main.scala (renamed from trunk/infrastructure/com.etherpad/main.scala) | 0 |
4 files changed, 456 insertions, 0 deletions
diff --git a/infrastructure/com.etherpad.openofficeservice/importexport.scala b/infrastructure/com.etherpad.openofficeservice/importexport.scala new file mode 100644 index 0000000..606cff9 --- /dev/null +++ b/infrastructure/com.etherpad.openofficeservice/importexport.scala @@ -0,0 +1,287 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.etherpad.openofficeservice; + +import net.appjet.common.sars.{SarsServer,SarsMessageHandler}; + +import java.io.{DataInputStream,DataOutputStream}; +import java.io.{File,FileOutputStream,ByteArrayInputStream,ByteArrayOutputStream}; + +/* Libraries needed for OO.org Conversion */ +import com.sun.star.bridge.{XBridge,XBridgeFactory}; +import com.sun.star.beans.{PropertyValue,XPropertySet}; +import com.sun.star.connection.{NoConnectException,XConnection,XConnector}; +import com.sun.star.container.XNamed; +import com.sun.star.document.{XExporter,XFilter}; +import com.sun.star.frame.{XComponentLoader,XStorable}; +import com.sun.star.lang.{XComponent,XMultiComponentFactory}; +import com.sun.star.uno.{UnoRuntime,XComponentContext}; + +class OOSException(m: String) extends RuntimeException(m); +class UnsupportedFormatException(format: String) extends OOSException("Unsupported format: "+format); +object TemporaryFailure extends OOSException("Temporary failure"); + +object OpenOfficeServerUtility { + def checkServerAvailability(host: String, port: Int): Boolean = { + // Assume the server is running; this is the responsibility of the user + return true; + } + def runOpenOfficeServer(path: String, host: String, port: Int, timeout: Int, wait: Boolean) { + // nothing + } +} + +class OpenOfficeFileConverter { + var host: String = "localhost"; + var port: Int = 8100; + + def setOpenOfficeServerDetails(host: String, port: Int) { + this.host = host; + this.port = port; + } + + def convertFile(src: File, dst: File, converter: String, extension: String): Boolean = { + try { + val fromFile: String = "file:///" + src.getAbsolutePath(); + val toFile: String = "file:///" + dst.getAbsolutePath(); + + val cnx: String = "socket,host="+this.host+",port="+this.port+""; + val xRemoteContext: XComponentContext = com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null); + val x: Object = xRemoteContext.getServiceManager().createInstanceWithContext("com.sun.star.connection.Connector", xRemoteContext); + val xConnector: XConnector = UnoRuntime.queryInterface(classOf[XConnector], x).asInstanceOf[XConnector]; + val connection: XConnection = xConnector.connect(cnx); + + if(connection == null) { + throw new OOSException("Connection failure"); + } + val x2: Object = xRemoteContext.getServiceManager().createInstanceWithContext("com.sun.star.bridge.BridgeFactory", xRemoteContext); + val xBridgeFactory: XBridgeFactory = UnoRuntime.queryInterface(classOf[XBridgeFactory], x2).asInstanceOf[XBridgeFactory]; + val xBridge: XBridge = xBridgeFactory.createBridge("", "urp", connection, null); + val x3: Object = xBridge.getInstance("StarOffice.ServiceManager"); + if (x3 == null) { + throw new OOSException("Failed to get bridge"); + } + + val xMultiComponentFactory: XMultiComponentFactory = UnoRuntime.queryInterface(classOf[XMultiComponentFactory], x3).asInstanceOf[XMultiComponentFactory]; + val xProperySet: XPropertySet = UnoRuntime.queryInterface(classOf[XPropertySet], xMultiComponentFactory).asInstanceOf[XPropertySet]; + val oDefaultContext: Object = xProperySet.getPropertyValue("DefaultContext"); + val xComponentContext: XComponentContext = UnoRuntime.queryInterface(classOf[XComponentContext], oDefaultContext).asInstanceOf[XComponentContext]; + + val desktopObj: Object = xMultiComponentFactory.createInstanceWithContext("com.sun.star.frame.Desktop", xComponentContext); + val xcomponentloader: XComponentLoader = UnoRuntime.queryInterface(classOf[XComponentLoader], desktopObj).asInstanceOf[XComponentLoader]; + + if(xcomponentloader == null) { + throw new OOSException("XComponent Loader could not be loaded"); + } + + val loadProps: Array[PropertyValue] = new Array[PropertyValue](2); + loadProps(0) = new PropertyValue(); + loadProps(0).Name = "Hidden"; + loadProps(0).Value = boolean2Boolean(false); + + loadProps(1) = new PropertyValue(); + loadProps(1).Name = "UpdateDocMode"; + loadProps(1).Value = "1"; + + val component: XComponent = xcomponentloader.loadComponentFromURL(fromFile,"_blank", 0, loadProps); + + if (component == null) { + throw new OOSException("Failed to load document"); + } + + val convProps: Array[PropertyValue] = new Array[PropertyValue](2); + convProps(0) = new PropertyValue(); + convProps(0).Name = "FilterName"; + convProps(0).Value = converter; + + val xstorable: XStorable = UnoRuntime.queryInterface(classOf[XStorable],component).asInstanceOf[XStorable]; + if (xstorable == null) { + throw new OOSException("Storable could not be loaded"); + } + xstorable.storeToURL(toFile, convProps); + component.dispose(); + return true; + } + catch { + case e => { + e.printStackTrace(); + throw new OOSException("Unknown exception occurred: "+e.getMessage()); + } + } + } +} + +object OpenOfficeService { + val formats = Map( + "pdf" -> "writer_pdf_Export", + "doc" -> "MS Word 97", + "html" -> "HTML (StarWriter)", + "odt" -> "writer8", + //"html" -> "XHTML Writer File", + "txt" -> "Text" + ); + + def createTempFile(bytes: Array[byte], suffix: String) = { + var f = File.createTempFile("ooconvert-", if (suffix == null) { null } else if (suffix == "") { "" } else { "."+suffix }); + if (bytes != null) { + val fos = new FileOutputStream(f); + fos.write(bytes); + } + f; + } + + var soffice = "soffice"; + def setExecutable(exec: String) { + soffice = exec; + } + + var openOfficeServerHost: String = "localhost"; + var openOfficeServerPort: Int = 8100; + + def setOpenOfficeServer(host: String, port: Int) { + openOfficeServerHost = host; + openOfficeServerPort = port; + } + + def convertFile(from: String, to: String, bytes: Array[byte]): Array[byte] = { + if (from == to) { + return bytes; + } + + val tempFile = createTempFile(bytes, from); + val outFile = createTempFile(null, to); + + /* + Just hardcoding server and port here. + If you intend to use an Openoffice.org instance on a network machine, + do it at your risk. + + Just, remember to setOpenOfficeServer from etherpad/importexport/importexport.js, + Also, remember that OO.org is reading and writing files over file:/// URI. So, make sure that + you can access the files from network machine. Hint, NFS. Not Need for Speed game, you idiot, + Network File System. + + */ + + if (! OpenOfficeServerUtility.checkServerAvailability(openOfficeServerHost, openOfficeServerPort)) { + try { + OpenOfficeServerUtility.runOpenOfficeServer(soffice, openOfficeServerHost, openOfficeServerPort, 20000, true); + } catch { + case e: java.io.IOException => { + e.printStackTrace(); + throw TemporaryFailure; + } + } + } + var converter = new OpenOfficeFileConverter(); + converter.setOpenOfficeServerDetails(openOfficeServerHost, openOfficeServerPort); + var status = false; + try { + status = converter.convertFile(tempFile, outFile, formats(to), to); + } catch { + case e => { + e.printStackTrace(); + throw new OOSException("Unknown exception occurred: "+e.getMessage()); + } + } + if (status == false) { + throw new UnsupportedFormatException(from); + } + net.appjet.common.util.BetterFile.getFileBytes(outFile); + } + + def main(args: Array[String]) { + if (args.length > 0) { + soffice = args(0); + if (soffice.length == 0) { + exit(1); + } + } + + // Query format: + // from: String, to: String, count: Int, bytes: Array[byte] + // Response format: + // status: Int, <data> + // status 0 (success) - <data>: count: Int, bytes: Array[byte] + // status 1 (temporary failure) - <data>: <none> + // status 2 (permanent failure) - <data>: type: Int + // type - 0: unknown failure. + // - 1: unsupported format + val handler = new SarsMessageHandler { + override def handle(b: Array[byte]): Option[Array[byte]] = { + val is = new DataInputStream(new ByteArrayInputStream(b)); + val from = is.readUTF; + val to = is.readUTF; + val len = is.readInt; + val bytes = new Array[byte](len); + is.readFully(bytes); + var status = 0; + var permfailuretype = 0; + + println("Converting "+from+" -> "+to+" ("+len+" bytes)"); + + val output = try { + convertFile(from, to, bytes); + } catch { + case TemporaryFailure => { + status = 1; + null; + } + case e: UnsupportedFormatException => { + status = 2; + permfailuretype = 1; + null; + } + case e => { + status = 2; + permfailuretype = 0; + e.printStackTrace(); + null; + } + } + + val retBytes = new ByteArrayOutputStream(); + val ret = new DataOutputStream(retBytes); + if (status != 0) { + ret.writeInt(status); // error + status match { + case 2 => { + ret.writeInt(permfailuretype); + } + case _ => { } + } + } else { + ret.writeInt(0); // success + ret.writeInt(output.length); + ret.write(output, 0, output.length); + } + Some(retBytes.toByteArray()); + } + } + + val server = new SarsServer("ooffice-password", handler, None, 8101); + server.start(); + println("Server running..."); + server.join(); + println("Server quitting..."); + } +} + + + + + diff --git a/trunk/infrastructure/com.etherpad/easysync2support.scala b/infrastructure/com.etherpad/easysync2support.scala index 9f1c527..9f1c527 100644 --- a/trunk/infrastructure/com.etherpad/easysync2support.scala +++ b/infrastructure/com.etherpad/easysync2support.scala diff --git a/infrastructure/com.etherpad/licensing.scala b/infrastructure/com.etherpad/licensing.scala new file mode 100644 index 0000000..68019f5 --- /dev/null +++ b/infrastructure/com.etherpad/licensing.scala @@ -0,0 +1,169 @@ +/** + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS-IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.etherpad; + +import net.appjet.oui.{Encryptomatic, config}; +import net.appjet.common.util.BetterFile; + +import java.io.{FileInputStream, FileOutputStream, ByteArrayInputStream, ByteArrayOutputStream, PrintWriter} + +import java.security._; +import java.security.spec._; + +object Licensing { + val publicKey = "s0dD94jKFjlSHIumgDQ4ldcyIyna1vMHmG5tsgkP987eBTW88XeEIUTo5JtWOjPzb252GURUrr7MReTqMz6NnsOupeJMqtXgjuVxcXbK8AnckxkxhRqMiFfBW39T9NzPgq09yBdH4tKGlPZQmgaBvjFI8cXTYa7a64LrDnrzrpDhDdJsZPZI2kE7G4vBCGijuhsZpyowK8zT5y2cKqIgIdLxUnXNFtayDi0oyuX1ywfBds2OEil9fEUQOQvkcHAt6kYfPXkE2XgQZFasAv0DPeWMUEtaHTbMaQn1U6BsfmsKjHlLhM3oWEzp0wEwUWxCC39iHYjxa5QKtxm5BNvUTTqJgkoEvk7Uu08j8jhFeCFosph6igDWPmyfAPKTnETXJobO2VON83bVHlX8UfKonnalMy0Hnw2C0I7c0UE0MtMIRtJxtwU62a311Ohp1EVrY4LwKIFfqRMVWKDP0cjXDkJyjJS58rC1DRU7SfPspgfuOy5YZo9sLKztXfzAPzNbXerQ24m2AjmYLV4JQked7MnaKQ6VfyZbFBK5th9NFcJwY1bGbIHW2EsKmiKUoNjPKRJ6VMC7odUCIXQyE9J"; + + val pkhash = "f7a3dd5940a3f79904b81e4d32a08e2efaa0b2ab"; + val keyVersion = 2.toByte; + + def thanksForStealingFromPoorHackersTryingToEkeAMeagerLivingFromThisCruelWorld = + Encryptomatic.bytesToAscii(MessageDigest.getInstance("SHA1").digest(publicKey.getBytes())) == pkhash; + def sha1(b: Array[Byte]): String = Encryptomatic.bytesToAscii(MessageDigest.getInstance("SHA1").digest(b)); + def sha1(s: String): String = sha1(s.getBytes("UTF-8")); + + def toBytes(i: Int): Array[Byte] = + Array((i >> 24).toByte, + (i >> 16).toByte, + (i >> 8).toByte, + i.toByte); + def toByte(i: Int): Array[Byte] = + Array(i.toByte); + def toBytes(l: Long): Array[Byte] = + Array((l >> 56).toByte, + (l >> 48).toByte, + (l >> 40).toByte, + (l >> 32).toByte, + (l >> 24).toByte, + (l >> 16).toByte, + (l >> 8).toByte, + l.toByte); + + def toInt(b0: Array[Byte]): Int = { + val b = b0.map(_.toInt & 0x00FF); + (b(0) << 24) | (b(1) << 16) | (b(2) << 8) | b(3); + } + def toInt(b: Byte): Int = b.toInt & 0x00FF; + + def toLong(b0: Array[Byte]): Long = { + val b = b0.map(_.toLong & 0x000000FF); + (b(0) << 56) | (b(1) << 48) | (b(2) << 40) | (b(3) << 32) | (b(4) << 24) | (b(5) << 16) | (b(6) << 8) | b(7); + } + + def generateKey(personName: String, organizationName: String, expiresDate: Long, editionId: Int, userQuota: Int, majorVersion: Int, minorVersion: Int, patchVersion: Int) = { + if (config.licenseGeneratorKey == null) { + throw new RuntimeException("No private key available to generate license key."); + } + def privateKey = Encryptomatic.readPrivateKey("DSA", new FileInputStream(config.licenseGeneratorKey)); + def clean(s: String) = s.replaceAll(":", "-"); + val keyPrefix = + List(personName, organizationName, expiresDate.toString, editionId.toString, userQuota.toString, majorVersion.toString, minorVersion.toString, patchVersion.toString).map(clean).mkString(":"); + val sig = Encryptomatic.sign(new ByteArrayInputStream(keyPrefix.getBytes("UTF-8")), privateKey) + + List(personName, organizationName).mkString(":") + ":" + + Encryptomatic.bytesToAscii( + Array.concat[Byte](Array(keyVersion), // don't want BigInt dropping bytes, that'd be sad. :( + toBytes(expiresDate), + toBytes(editionId), + toBytes(userQuota), + toByte(majorVersion), + toByte(minorVersion), + toByte(patchVersion), + sig)); + } + + def decodeKey(key: String) = try { + val Array(personName0, organizationName0, sigAndInfo) = key.split(":"); + val sigAndInfoBytes = Encryptomatic.asciiToBytes(sigAndInfo); + val thisKeyVersion = toInt(sigAndInfoBytes(0)); + val expiresDate0 = toLong(sigAndInfoBytes.slice(1, 9)); + val editionId0 = toInt(sigAndInfoBytes.slice(9, 13)); + val userQuota0 = toInt(sigAndInfoBytes.slice(13, 17)); + val (majorVersion0, minorVersion0, patchVersion0) = + if (thisKeyVersion >= 2) { + (toInt(sigAndInfoBytes(17)), toInt(sigAndInfoBytes(18)), toInt(sigAndInfoBytes(19))); + } else { + (0, 0, 0); + } + val sig = sigAndInfoBytes.drop(if (thisKeyVersion >= 2) 20 else 17); + val keyPrefix = { + var a = Seq(personName0, organizationName0, expiresDate0.toString, editionId0.toString, userQuota0.toString); + if (thisKeyVersion >= 2) { + a = a ++ Seq(majorVersion0.toString, minorVersion0.toString, patchVersion0.toString); + } + a.mkString(":"); + } + if (! Encryptomatic.verify(new ByteArrayInputStream(keyPrefix.getBytes("UTF-8")), + Encryptomatic.readPublicKey("DSA", + new ByteArrayInputStream(publicKey.getBytes())), sig)) { + null; + } else { + new { + def personName = personName0; + def organizationName = organizationName0; + def expiresDate = expiresDate0; + def editionId = editionId0; + def userQuota = userQuota0; + def majorVersion = majorVersion0; + def minorVersion = minorVersion0; + def patchVersion = patchVersion0; + } + } + } catch { + case e => null; + } + + def main(args: Array[String]) { + args(0) match { + case "genkeypair" => { + println("Generating keypair..."); + Encryptomatic.writeKeyPair(Encryptomatic.generateKeyPair("DSA"), args(1), args(2)); + println("Done."); + } + case "genmainkey" => { + println("Generating main key..."); + config.values("licenseGeneratorKey") = args(1); + val out = new PrintWriter(new FileOutputStream(args(2))); + out.print(generateKey("etherpad", "AppJet", -1, 0, -1, 0, 0, 0)) + out.close(); + println("Done."); + } + case "test" => { + println("Testing key generation."); + config.values("licenseGeneratorKey") = args(1); + val key = generateKey("Foo Bar", "Baz, Inc.", System.currentTimeMillis() + 86400*1000, 0, 100, 1, 2, 3); + println("Key is: "+key); + val obj = decodeKey(key); + println(List(obj.personName, obj.organizationName, obj.expiresDate, obj.editionId, obj.userQuota, obj.majorVersion, obj.minorVersion, obj.patchVersion).mkString(", ")); + } + case "parsekey" => { + println("Testing key decode."); + val obj = decodeKey(args(1)); + println("Key: "+List(obj.personName, obj.organizationName, obj.expiresDate, obj.editionId, obj.userQuota, obj.majorVersion, obj.minorVersion, obj.patchVersion).mkString(", ")); + } + case "testascii" => { + val one = 17; + val two = -1L; + val three = (Math.random*Math.pow(10, (Math.random*10).toInt)).toInt; + println(List(one, two, three).mkString(", ")); + println(List(toInt(toBytes(one)), toLong(toBytes(two)), toInt(toBytes(three))).mkString(", ")); + val bytes = Encryptomatic.asciiToBytes(Encryptomatic.bytesToAscii(Array.concat[Byte](Array(1.toByte), toBytes(one), toBytes(two), toBytes(three)))); + println("I can has bytes: "+bytes.length); + println(List(toInt(bytes.slice(1, 5)), toLong(bytes.slice(5, 13)), toInt(bytes.slice(13, 17))).mkString(", ")); + } + } + } +} diff --git a/trunk/infrastructure/com.etherpad/main.scala b/infrastructure/com.etherpad/main.scala index 5110aba..5110aba 100644 --- a/trunk/infrastructure/com.etherpad/main.scala +++ b/infrastructure/com.etherpad/main.scala |