{ "id": "91758", "key": "TIMOB-9111", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "17609", "description": "", "name": "Release 7.0.0", "archived": false, "released": true, "releaseDate": "2017-12-07" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2017-11-15T20:47:31.000+0000", "created": "2012-05-11T12:28:30.000+0000", "priority": { "name": "High", "id": "2" }, "labels": [ "SupportTeam", "core", "parity" ], "versions": [ { "id": "13272", "description": "Release 2.0.1", "name": "Release 2.0.1", "archived": true, "released": true, "releaseDate": "2012-04-16" }, { "id": "14162", "description": "Release 3.1.0", "name": "Release 3.1.0", "archived": true, "released": true, "releaseDate": "2013-04-16" }, { "id": "14982", "description": "Release 3.2.0", "name": "Release 3.2.0", "archived": false, "released": true, "releaseDate": "2013-12-19" } ], "issuelinks": [ { "id": "56146", "type": { "id": "10001", "name": "Cloners", "inward": "is cloned into", "outward": "is cloned from" }, "inwardIssue": { "id": "170317", "key": "TIMOB-25513", "fields": { "summary": "Windows: Ti.Utils.base64encode behaves different between Android/iOS and Windows", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "High", "id": "2" }, "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false } } } }, { "id": "52033", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "outwardIssue": { "id": "161057", "key": "TIMOB-23508", "fields": { "summary": "iOS: Use native NSData base64 encode API to replace our current methods", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "None", "id": "6" }, "issuetype": { "id": "4", "description": "An improvement or enhancement to an existing feature or task.", "name": "Improvement", "subtask": false } } } } ], "assignee": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "updated": "2017-11-17T21:45:59.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "description": "h3. Problem\r\n\r\nThe result of Ti.Utils.base64encode is different between iOS and Android. On iOS there are newline characters included when the string is long enough, on Android the newline characters are stripped out (which is the correct way to do base64 UTF8 encoding). Please see the attached screenshot explaining the issue. \r\n\r\nh3. Sample Code\r\n\r\n{noformat}\r\n\r\n(function() {\r\n \r\n var win = Ti.UI.createWindow({backgroundColor:'white'});\r\n win.open();\r\n var shortString = 'ABCDEFGHIJ1234567890ABCDEFGHIJ12|psndemo2|abcd:1';\r\n var longString = 'ABCDEFGHIJ1234567890ABCDEFGHIJ12|psndemo2|abcd:12345678901234567890';\r\n var tiBase64ShortResult = Ti.Utils.base64encode(shortString);\r\n var tiBase64LongResult = Ti.Utils.base64encode(longString);\r\n\r\n tiBase64ShortResult = String(tiBase64ShortResult);\r\n tiBase64LongResult = String(tiBase64LongResult);\r\n\r\n\r\n // BUG #2: Run this code in iOS and Android and compare the results\r\n // On iOS we get newline characters. On Android the newline characters are stripped out.\r\n // The correct behavior for UTF8 base64 encoding is to strip out the newline characters. \r\n // Android's implementation is correct.\r\n tiBase64ShortResult = tiBase64ShortResult.replace(/\\r\\n/g,\"***NEWLINE***\");\r\n tiBase64LongResult = tiBase64LongResult.replace(/\\r\\n/g,\"***NEWLINE***\");\r\n var alertDialog = Titanium.UI.createAlertDialog(\r\n {\r\n title: 'Result', \r\n message: 'short: ' + tiBase64ShortResult + '\\nlong: ' + tiBase64LongResult, buttonNames: ['OK','Cancel'] \r\n });\r\n alertDialog.show();\r\n})();\r\n\r\n{noformat}", "attachment": [ { "id": "63519", "filename": "encodedFile.txt", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-11-01T16:35:09.000+0000", "size": 29, "mimeType": "text/plain" }, { "id": "27653", "filename": "Screen shot 2012-05-08 at 3.43.47 PM.png", "author": { "name": "vjoshi", "key": "vjoshi", "displayName": "Varun Joshi", "active": true, "timeZone": "America/New_York" }, "created": "2012-05-11T12:28:30.000+0000", "size": 690743, "mimeType": "image/png" } ], "flagged": false, "summary": "TiAPI: Ti.Utils.base64encode behaves different between Android and iOS/Android", "creator": { "name": "vjoshi", "key": "vjoshi", "displayName": "Varun Joshi", "active": true, "timeZone": "America/New_York" }, "subtasks": [], "reporter": { "name": "vjoshi", "key": "vjoshi", "displayName": "Varun Joshi", "active": true, "timeZone": "America/New_York" }, "environment": "Titanium SDK: 2.0.1GA2\r\nPlatform OS: iOS ", "closedSprints": [ { "id": 796, "state": "closed", "name": "2017 Sprint 02 SDK", "startDate": "2017-01-15T00:00:41.845Z", "endDate": "2017-01-29T00:00:00.000Z", "completeDate": "2017-01-30T21:10:44.640Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "225398", "author": { "name": "shalvika", "key": "shalvika", "displayName": "Shalvika", "active": true, "timeZone": "Asia/Kolkata" }, "body": "Could be because of the problem mentioned here http://developer.appcelerator.com/question/120731/xhr-authentication-with-restful-api\r\n\r\n\"It's also important to note that Titanium.Utils.base64encode() adds line breaks every 72 characters (bug?)\"", "updateAuthor": { "name": "shalvika", "key": "shalvika", "displayName": "Shalvika", "active": true, "timeZone": "Asia/Kolkata" }, "created": "2012-10-29T20:12:53.000+0000", "updated": "2012-10-29T20:12:53.000+0000" }, { "id": "247249", "author": { "name": "jithinv@exalture.com", "key": "jithinv@exalture.com", "displayName": "jithinpv", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Issue reproduces \n\nTested with\n\nTitanium Studio, build: 3.0.1.201212181159\nTitanium SDK version: 3.1.0 \nTitanium SDK version: 3.0.2 \niOS iPhone Simulator: iOS SDK version: 6.0", "updateAuthor": { "name": "jithinv@exalture.com", "key": "jithinv@exalture.com", "displayName": "jithinpv", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-04-12T07:22:33.000+0000", "updated": "2013-04-12T07:22:33.000+0000" }, { "id": "382070", "author": { "name": "fokkezb", "key": "fokke", "displayName": "Fokke Zandbergen", "active": true, "timeZone": "Europe/Amsterdam" }, "body": "Can we get this scheduled please? After verifying newlines indeed shouldn't be included in base64encoded data, all we need to do is remove these here:\r\n\r\nhttps://github.com/appcelerator/titanium_mobile/blob/415bd6c66dcc55b1a59a59574f3babd3c3a84ede/iphone/Classes/Base64Transcoder.c#L132-L136\r\n\r\nAlso see:\r\n\r\n* http://stackoverflow.com/a/20065991/4626813\r\n* http://stackoverflow.com/a/4680044/4626813\r\n* https://en.wikipedia.org/wiki/Base64#Implementations_and_history\r\n\r\nSeems like it depends on the implementation. Perhaps we should make it an option.", "updateAuthor": { "name": "fokkezb", "key": "fokke", "displayName": "Fokke Zandbergen", "active": true, "timeZone": "Europe/Amsterdam" }, "created": "2016-04-07T13:44:27.000+0000", "updated": "2016-04-07T13:49:26.000+0000" }, { "id": "382166", "author": { "name": "nitrag", "key": "nitrag", "displayName": "Ryan Gartin", "active": true, "timeZone": "America/New_York" }, "body": "BUMP! I've wasted 8+ hours only to find out this is the issue! HALP!", "updateAuthor": { "name": "nitrag", "key": "nitrag", "displayName": "Ryan Gartin", "active": true, "timeZone": "America/New_York" }, "created": "2016-04-08T03:18:24.000+0000", "updated": "2016-04-08T03:18:24.000+0000" }, { "id": "384126", "author": { "name": "apetkov", "key": "apetkov", "displayName": "Angel Petkov", "active": true, "timeZone": "America/Los_Angeles" }, "body": "PR: https://github.com/appcelerator/titanium_mobile/pull/7976\r\n\r\nDemo Code:\r\n{code:javascript}\r\nvar win = Ti.UI.createWindow({backgroundColor:'white'});\r\nvar shortString = 'ABCDEFGHIJ1234567890ABCDEFGHIJ12|psndemo2|abcd:1';\r\nvar longString = 'ABCDEFGHIJ1234567890ABCDEFGHIJ12|psndemo2|abcd:12345678901234567890';\r\nvar tiBase64ShortResult = Ti.Utils.base64encode(shortString);\r\nvar tiBase64LongResult = Ti.Utils.base64encode(longString);\r\n\r\ntiBase64ShortResult = String(tiBase64ShortResult);\r\ntiBase64LongResult = String(tiBase64LongResult);\r\n\r\nvar label2 = Ti.UI.createLabel({\r\n color:'blue',\r\n text: 'short: ' + tiBase64ShortResult + '\\nlong: ' + tiBase64LongResult,\r\n textAlign: Ti.UI.TEXT_ALIGNMENT_LEFT,\r\n top: 30,\r\n width: 300, height: 200\r\n});\r\nwin.add(label2);\r\nwin.open()\r\n{code}", "updateAuthor": { "name": "apetkov", "key": "apetkov", "displayName": "Angel Petkov", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-04-27T22:47:01.000+0000", "updated": "2016-04-27T22:47:19.000+0000" }, { "id": "386821", "author": { "name": "hpham", "key": "hpham", "displayName": "Hieu Pham", "active": true, "timeZone": "America/Los_Angeles" }, "body": "What is the expected value of the demo above? And are the results going to be the same on both Android and iOS? ", "updateAuthor": { "name": "hpham", "key": "hpham", "displayName": "Hieu Pham", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-05-25T22:42:12.000+0000", "updated": "2016-05-25T22:42:12.000+0000" }, { "id": "386823", "author": { "name": "apetkov", "key": "apetkov", "displayName": "Angel Petkov", "active": true, "timeZone": "America/Los_Angeles" }, "body": "There should just be no more new lines added redundantly, yes I believe they should be. There is more information available regarding the return values on the PR. ", "updateAuthor": { "name": "apetkov", "key": "apetkov", "displayName": "Angel Petkov", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-05-25T22:46:28.000+0000", "updated": "2016-05-25T22:49:22.000+0000" }, { "id": "386824", "author": { "name": "hpham", "key": "hpham", "displayName": "Hieu Pham", "active": true, "timeZone": "America/Los_Angeles" }, "body": "I'm saying that you should include the expected value inside the demo so when QE test it, they know what to expect, and if somehow they get a different value, then they would know its a bug.", "updateAuthor": { "name": "hpham", "key": "hpham", "displayName": "Hieu Pham", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-05-25T22:50:37.000+0000", "updated": "2016-05-25T22:50:37.000+0000" }, { "id": "387369", "author": { "name": "cng", "key": "cng", "displayName": "Chee Kiat Ng", "active": false, "timeZone": "America/Los_Angeles" }, "body": "[~apetkov] What's the status of this?", "updateAuthor": { "name": "cng", "key": "cng", "displayName": "Chee Kiat Ng", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2016-06-02T02:27:30.000+0000", "updated": "2016-06-02T02:27:30.000+0000" }, { "id": "387958", "author": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "body": "I posted some discussion about this on the PR, but I wanted to summarize here:\r\n\r\n- I think if anything, the fix should be to Android to default to adding newlines to split the output up. There are a number of uses/specs for base64:\r\n - https://tools.ietf.org/html/rfc4648#page-3\r\n - https://en.wikipedia.org/wiki/Base64#Variants_summary_table\r\n\r\nSome of those specs *require* max line lengths (specifically 76 or 64 characters max per line).\r\n\r\niOS and Windows both default to adding newlines to break up the output. So really, Android is the odd one out in comparison. Or, I suppose we can choose to default to no newlines, but would need to change both iOS and Windows.\r\n\r\n- I think we should modify the base64 method to take a second optional Dictionary argument to control behavior like this. For this who need a specific line length, or want to turn off newlines, or use different padding characters or characters for indices 62/63 - so that they could match the listed specs at https://en.wikipedia.org/wiki/Base64#Variants_summary_table\r\n\r\nAndroid's base64 does have some options around some of this: https://developer.android.com/reference/android/util/Base64.html\r\n\r\nBasically it looks like the defaults for encoding should hopefully be the options that'd work across most applications: max 76-char lines, use CRLF to break lines, use '=' for padding character, use '+' and '/' for indices 62 and 63. Then we could add options for: no newlines, URL safe chars ('-' and '_' for 62/63), etc.", "updateAuthor": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "created": "2016-06-09T17:29:44.000+0000", "updated": "2016-06-09T17:29:44.000+0000" }, { "id": "426279", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "Moving to 7.0.0 since this will be a (minor) breaking change on iOS and Windows. We will remove the \\n characters to align with modern conversions. It will also eliminate possible issues with using those strings in the web-view (especially Ti.WKWebView).", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-08-08T21:17:13.000+0000", "updated": "2017-08-08T21:17:13.000+0000" }, { "id": "426286", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "PR (titanium_verify/master): https://github.com/appcelerator/titanium_verify/pull/25\r\nPR (titanium_mobile/master): https://github.com/appcelerator/titanium_mobile/pull/9288\r\n\r\nTest-case ({{encodedFile.txt}} is attached:\r\n{code:js}\r\nvar win = Ti.UI.createWindow({\r\n backgroundColor: '#fff'\r\n});\r\n\r\nvar btn = Ti.UI.createButton({\r\n title: 'Trigger'\r\n});\r\n\r\nbtn.addEventListener('click', function() {\r\n Ti.API.info('--- ENCODE! --- ');\r\n \r\n var clearText = 'test';\r\n var encoded = 'dGVzdA==';\r\n \r\n var test = Ti.Utils.base64encode(clearText);\r\n \r\n if (test.apiName !== 'Ti.Blob') {\r\n Ti.API.error('test is not a Ti.Blob');\r\n Ti.API.error(test.apiName);\r\n } else {\r\n Ti.API.warn('Test 1/5 passed');\r\n }\r\n \r\n if (test.getText() !== encoded) {\r\n Ti.API.error('test is not properly encoded');\r\n Ti.API.error(test.getText());\r\n } else {\r\n Ti.API.warn('Test 2/5 passed');\r\n }\r\n \r\n Ti.API.info('--- DECODE! --- ');\r\n \r\n test = Ti.Utils.base64decode(encoded);\r\n\r\n if (test.apiName !== 'Ti.Blob') {\r\n Ti.API.error('test is not a Ti.Blob');\r\n Ti.API.error(test.apiName);\r\n } else {\r\n Ti.API.warn('Test 3/5 passed');\r\n }\r\n\r\n if (test.getText() !== clearText) {\r\n Ti.API.error('test is not properly decoded');\r\n Ti.API.error(test.getText());\r\n } else {\r\n Ti.API.warn('Test 4/5 passed');\r\n }\r\n \r\n var f = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory, 'encodedFile.txt');\r\n var blob = Ti.Utils.base64decode(f.read());\r\n \r\n if (blob.toString() !== 'Decoding successful!') {\r\n Ti.API.error('Blob-decode not working!');\r\n Ti.API.error(blob.toString());\r\n } else {\r\n Ti.API.warn('Test 5/5 passed');\r\n }\r\n});\r\n\r\nwin.add(btn);\r\nwin.open();\r\n{code}", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-08-08T22:03:24.000+0000", "updated": "2017-11-01T16:34:58.000+0000" }, { "id": "430648", "author": { "name": "kiguchi", "key": "kota", "displayName": "Kota Iguchi", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Cloned this for Windows: TIMOB-25513 with fixVersion:none since I don't think we can slip this in to Windows within 7.0.0 time frame.", "updateAuthor": { "name": "kiguchi", "key": "kota", "displayName": "Kota Iguchi", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-11-15T04:06:16.000+0000", "updated": "2017-11-15T04:06:16.000+0000" }, { "id": "430650", "author": { "name": "amukherjee", "key": "amukherjee", "displayName": "Abir Mukherjee", "active": true, "timeZone": "America/Los_Angeles" }, "body": "FR passed. [~kiguchi] thanks for creating the ticket for Windows. Currently looking into unit test issues.", "updateAuthor": { "name": "amukherjee", "key": "amukherjee", "displayName": "Abir Mukherjee", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2017-11-15T04:18:08.000+0000", "updated": "2017-11-15T19:37:10.000+0000" }, { "id": "430950", "author": { "name": "ewieber", "key": "ewieber", "displayName": "Eric Wieber", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Verified in SDK build 7.0.0.v20171117124508", "updateAuthor": { "name": "ewieber", "key": "ewieber", "displayName": "Eric Wieber", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-11-17T21:45:58.000+0000", "updated": "2017-11-17T21:45:58.000+0000" } ], "maxResults": 21, "total": 21, "startAt": 0 } } }