{"_id":"56e97bc5d825061900d1ac84","category":{"_id":"56e97ba8d825061900d1ac83","pages":["56e97bc5d825061900d1ac84"],"project":"552829408962f339009a678d","version":"552829408962f339009a6790","__v":1,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2016-03-16T15:28:40.021Z","from_sync":false,"order":11,"slug":"api-docs","title":"Client API"},"user":"55282916d9e1db2d00cd923c","project":"552829408962f339009a678d","parentDoc":null,"version":{"_id":"552829408962f339009a6790","project":"552829408962f339009a678d","__v":26,"createdAt":"2015-04-10T19:49:20.516Z","releaseDate":"2015-04-10T19:49:20.516Z","categories":["552829418962f339009a6791","55284ed68962f339009a67e1","55286c73391a362500d9b3f4","552918f6b316811900149f59","5529b255d739240d00a3483e","553287590a578a0d008d4ff5","55329385e7d1fa0d003fc946","5550b55200420e0d00d1312f","55525fca953c9c0d00f507d7","559199695631432f002d358a","559d8d96980b801700d5ec7e","55c5e833cccdeb2d004e24b9","55d76504f662951900fc0e7d","55ea213cc62aa02f008229cd","56157b750f5ed00d00483dd8","561981fbac0924170069f4e8","561b8b1ea430930d0037ea67","563417428b86331700b488ca","56cd785bface161300dae0ec","56cdcc6e70db8a15006395f4","56cdf1b749abf10b0036a34a","56cedc8ce50c9c1b00830423","56e97ba8d825061900d1ac83","570d505228e6900e00477229","573614ca2ab52e1700c8e851","57d556a2496a3117004d70cf"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.0.0","version":"1.0"},"__v":12,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2016-03-16T15:29:09.817Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":0,"body":"[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Request file url builder params\"\n}\n[/block]\nTo request file url builder parameters, make `RequestGetFileUrlBuilder`\nIt will respond  with:\n```\nResponseGetFileUrlBuilder(baseUrl: String, algo: String,signatureSecret: ByteArray, timeout: Int, seed:String)\n```\n* **baseUrl** - url that client should send request to\n* **algo** - MAC algorithm used to generate signature. Currently supported - HMAC_SHA256\n* **signatureSecret** - secret used to generate signature in bytes\n* **timeout** - the seconds from the epoch of 1970-01-01T00:00:00Z. File url builder will become expired after this date\n* **seed** - secret seed in hex string. Required to validate secret on file requests\n\nSeed is calculated following way: `version + expireAt + randomString`, where:\n* **version** - version of algorithm(current version is 0)\n* **expireAt** - the seconds from the epoch of 1970-01-01T00:00:00Z, File url builder will become expired after this date\n* **randomString** - random string to introduce entropy\n\nSecret is calculated  following way: `HMAC(seed, serverSecret)`, where:\n* **seed** - seed produced on previous step\n* **serverSecret** - server secret\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Request file via http api\"\n}\n[/block]\nWhen client got FileIUrlBuilder params, he must generate signature and url to file he want to download.\n\nSignature generated following way:\n`signature = hex(seed) + \"_\" + hex(HMAC(seed + fileId + accessHash, secret))`\n\nGenerated url will be:\n`<baseUrl>/<fileId>?signature=<signature>`\nExample url:\n```\nhttps://api.actor.im/v1/files/5930642139438289453/?signature=1cf51b326132c0d_b6545b33837ebebd70b66b346a47d1327f71df57e8915ea25\n```\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Signature calculation example\"\n}\n[/block]\nWhen server responded with file url builder with:\n* base url: http://localhost:9090/v1/files\n* secret: ```{ 21, 85, 18, -3, -24, 6, 50, -49, 57, -20, -74, -121, -23, 1, -28, -3, 123, -4, 95, -27, 125, 74, -41, 92, -59, -5, 72, 76, 60, -104, -52, 123 }```\n* algo: HMAC_SHA256\n* seed: ```080010acb183b9051a2839313330393138373136353165393738636562343336383461373636323039333936343964343333```\n* timeout: 1461770412\n\nand we have file with:\n* name: avatar\n* file id: -8546473890980850083\n* file access hash: -5006470655828232781\n\nFile builder uri will be: ```http://localhost:9090/v1/files/-8546473890980850083?signature=080010acb183b9051a2839313330393138373136353165393738636562343336383461373636323039333936343964343333_3a08046fc12a10474128e13548c36c61e677dc53422899d625ad8f352948baa1```\n\nFollowing this link will respond with:\n* Status: 302(Found)\n* Location header: ```http://localhost:9090/v1/files/-8546473890980850083/avatar?signature=ffa3c06ac6d567eb9157e53ff47f2253246f0ff7c0b7feed995423d33570b5a6&expires=1461853213540```\n\n## Calculation example in Java code\n\n* [Class in test code](https://github.com/actorapp/actor-platform/blob/6b8f60c5fc269b145437edcabe92a8a4843a348a/actor-server/actor-tests/src/test/scala/im/actor/server/api/rpc/service/files/CalcSignature.java)\n* [Test using this signature calculation](https://github.com/actorapp/actor-platform/blob/6b8f60c5fc269b145437edcabe92a8a4843a348a/actor-server/actor-tests/src/test/scala/im/actor/server/api/rpc/service/files/FileBuilderSpec.scala)\n\n```\nimport org.apache.commons.codec.digest.HmacUtils;\nimport org.apache.commons.lang3.ArrayUtils;\n\nimport java.nio.ByteBuffer;\n\npublic class CalcSignature {\n\n    /**\n     * Returns url with calculated signature for specific file with specific file builder parameters\n     * :::at:::param baseUri base uri from file url builder\n     * @param seed seed provided by file url builder. Must be included in url\n     * @param signatureSecret secret used to sign request\n     * @param fileId id of file to download\n     * @param fileAccessHash access hash of file to download\n     * @return file url\n     */\n    public static String fileBuilderUrl(String baseUri, String seed, byte[] signatureSecret, long fileId, long fileAccessHash) {\n        byte[] seedBytes = decodeHex(seed.toCharArray());\n        byte[] fileIdBytes = getBytes(fileId);\n        byte[] accessHashBytes = getBytes(fileAccessHash);\n\n        byte[] bytesToSign = ArrayUtils.addAll(ArrayUtils.addAll(seedBytes, fileIdBytes), accessHashBytes);\n\n        String signPart = HmacUtils.hmacSha256Hex(signatureSecret, bytesToSign);\n\n        String signature = seed + \"_\" + signPart;\n\n        return baseUri + \"/\" + fileId + \"?signature=\" + signature;\n    }\n\n    private static byte[] decodeHex(final char[] data) {\n\n        final int len = data.length;\n\n        if ((len & 0x01) != 0) {\n            throw new RuntimeException(\"Odd number of characters.\");\n        }\n\n        final byte[] out = new byte[len >> 1];\n\n        // two characters form the hex value.\n        for (int i = 0, j = 0; j < len; i++) {\n            int f = toDigit(data[j], j) << 4;\n            j++;\n            f = f | toDigit(data[j], j);\n            j++;\n            out[i] = (byte) (f & 0xFF);\n        }\n\n        return out;\n    }\n\n    private static int toDigit(final char ch, final int index) {\n        final int digit = Character.digit(ch, 16);\n        if (digit == -1) {\n            throw new RuntimeException(\"Illegal hexadecimal character \" + ch + \" at index \" + index);\n        }\n        return digit;\n    }\n\n    private static byte[] getBytes(long value) {\n        return ByteBuffer.allocate(Long.BYTES).putLong(value).array();\n    }\n\n}\n```","excerpt":"API for downloading and uploading files with Actor","slug":"api-files","type":"basic","title":"File API"}

File API

API for downloading and uploading files with Actor

[block:api-header] { "type": "basic", "title": "Request file url builder params" } [/block] To request file url builder parameters, make `RequestGetFileUrlBuilder` It will respond with: ``` ResponseGetFileUrlBuilder(baseUrl: String, algo: String,signatureSecret: ByteArray, timeout: Int, seed:String) ``` * **baseUrl** - url that client should send request to * **algo** - MAC algorithm used to generate signature. Currently supported - HMAC_SHA256 * **signatureSecret** - secret used to generate signature in bytes * **timeout** - the seconds from the epoch of 1970-01-01T00:00:00Z. File url builder will become expired after this date * **seed** - secret seed in hex string. Required to validate secret on file requests Seed is calculated following way: `version + expireAt + randomString`, where: * **version** - version of algorithm(current version is 0) * **expireAt** - the seconds from the epoch of 1970-01-01T00:00:00Z, File url builder will become expired after this date * **randomString** - random string to introduce entropy Secret is calculated following way: `HMAC(seed, serverSecret)`, where: * **seed** - seed produced on previous step * **serverSecret** - server secret [block:api-header] { "type": "basic", "title": "Request file via http api" } [/block] When client got FileIUrlBuilder params, he must generate signature and url to file he want to download. Signature generated following way: `signature = hex(seed) + "_" + hex(HMAC(seed + fileId + accessHash, secret))` Generated url will be: `<baseUrl>/<fileId>?signature=<signature>` Example url: ``` https://api.actor.im/v1/files/5930642139438289453/?signature=1cf51b326132c0d_b6545b33837ebebd70b66b346a47d1327f71df57e8915ea25 ``` [block:api-header] { "type": "basic", "title": "Signature calculation example" } [/block] When server responded with file url builder with: * base url: http://localhost:9090/v1/files * secret: ```{ 21, 85, 18, -3, -24, 6, 50, -49, 57, -20, -74, -121, -23, 1, -28, -3, 123, -4, 95, -27, 125, 74, -41, 92, -59, -5, 72, 76, 60, -104, -52, 123 }``` * algo: HMAC_SHA256 * seed: ```080010acb183b9051a2839313330393138373136353165393738636562343336383461373636323039333936343964343333``` * timeout: 1461770412 and we have file with: * name: avatar * file id: -8546473890980850083 * file access hash: -5006470655828232781 File builder uri will be: ```http://localhost:9090/v1/files/-8546473890980850083?signature=080010acb183b9051a2839313330393138373136353165393738636562343336383461373636323039333936343964343333_3a08046fc12a10474128e13548c36c61e677dc53422899d625ad8f352948baa1``` Following this link will respond with: * Status: 302(Found) * Location header: ```http://localhost:9090/v1/files/-8546473890980850083/avatar?signature=ffa3c06ac6d567eb9157e53ff47f2253246f0ff7c0b7feed995423d33570b5a6&expires=1461853213540``` ## Calculation example in Java code * [Class in test code](https://github.com/actorapp/actor-platform/blob/6b8f60c5fc269b145437edcabe92a8a4843a348a/actor-server/actor-tests/src/test/scala/im/actor/server/api/rpc/service/files/CalcSignature.java) * [Test using this signature calculation](https://github.com/actorapp/actor-platform/blob/6b8f60c5fc269b145437edcabe92a8a4843a348a/actor-server/actor-tests/src/test/scala/im/actor/server/api/rpc/service/files/FileBuilderSpec.scala) ``` import org.apache.commons.codec.digest.HmacUtils; import org.apache.commons.lang3.ArrayUtils; import java.nio.ByteBuffer; public class CalcSignature { /** * Returns url with calculated signature for specific file with specific file builder parameters * @param baseUri base uri from file url builder * @param seed seed provided by file url builder. Must be included in url * @param signatureSecret secret used to sign request * @param fileId id of file to download * @param fileAccessHash access hash of file to download * @return file url */ public static String fileBuilderUrl(String baseUri, String seed, byte[] signatureSecret, long fileId, long fileAccessHash) { byte[] seedBytes = decodeHex(seed.toCharArray()); byte[] fileIdBytes = getBytes(fileId); byte[] accessHashBytes = getBytes(fileAccessHash); byte[] bytesToSign = ArrayUtils.addAll(ArrayUtils.addAll(seedBytes, fileIdBytes), accessHashBytes); String signPart = HmacUtils.hmacSha256Hex(signatureSecret, bytesToSign); String signature = seed + "_" + signPart; return baseUri + "/" + fileId + "?signature=" + signature; } private static byte[] decodeHex(final char[] data) { final int len = data.length; if ((len & 0x01) != 0) { throw new RuntimeException("Odd number of characters."); } final byte[] out = new byte[len >> 1]; // two characters form the hex value. for (int i = 0, j = 0; j < len; i++) { int f = toDigit(data[j], j) << 4; j++; f = f | toDigit(data[j], j); j++; out[i] = (byte) (f & 0xFF); } return out; } private static int toDigit(final char ch, final int index) { final int digit = Character.digit(ch, 16); if (digit == -1) { throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); } return digit; } private static byte[] getBytes(long value) { return ByteBuffer.allocate(Long.BYTES).putLong(value).array(); } } ```