├── .gitignore ├── AddToTransmission.safariextension ├── Icon.png ├── se.zanmato.addtotransmission.css ├── Info.plist ├── Settings.plist ├── se.zanmato.addtotransmission.js └── global.html ├── AddToTransmission-Firefox ├── content │ ├── brand │ │ ├── icon.png │ │ └── context-icon.png │ ├── overlay.xul │ ├── style.css │ ├── options.xul │ ├── overlay.js │ └── torrentClient.js ├── config_build.sh ├── locale │ ├── sv-SE │ │ ├── overlay.properties │ │ └── overlay.dtd │ └── en-US │ │ ├── overlay.properties │ │ └── overlay.dtd ├── chrome.manifest ├── defaults │ └── preferences │ │ └── pref.js ├── install.rdf ├── components │ └── magnetService.js └── build_mac.sh ├── Update.plist └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | AddToTransmission.safariextension/.DS_Store 2 | -------------------------------------------------------------------------------- /AddToTransmission.safariextension/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zanmato/Add-to-Transmission/HEAD/AddToTransmission.safariextension/Icon.png -------------------------------------------------------------------------------- /AddToTransmission-Firefox/content/brand/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zanmato/Add-to-Transmission/HEAD/AddToTransmission-Firefox/content/brand/icon.png -------------------------------------------------------------------------------- /AddToTransmission-Firefox/content/brand/context-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zanmato/Add-to-Transmission/HEAD/AddToTransmission-Firefox/content/brand/context-icon.png -------------------------------------------------------------------------------- /AddToTransmission-Firefox/config_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Build config for the build script, build.sh. Look there for more info. 4 | 5 | APP_NAME=addtotransmission 6 | CHROME_PROVIDERS="content locale defaults" 7 | CLEAN_UP=1 8 | ROOT_FILES= 9 | ROOT_DIRS= 10 | BEFORE_BUILD= 11 | AFTER_BUILD= 12 | -------------------------------------------------------------------------------- /AddToTransmission-Firefox/locale/sv-SE/overlay.properties: -------------------------------------------------------------------------------- 1 | downloadingString=Hämtar 2 | failedFetchString=Kunde inte hämta torrenten 3 | sendingString=Skickar 4 | unauthorizedString=Kunde inte logga in 5 | emptyResponseString=Tomt svar 6 | couldNotConnectString=Kunde inte ansluta till Transmission 7 | invalidTorrentString=Fel på torrent-filen 8 | duplicateTorrentString=Redan tillagd 9 | unknownErrorString=Okänt fel 10 | successString=Tillagd -------------------------------------------------------------------------------- /AddToTransmission-Firefox/chrome.manifest: -------------------------------------------------------------------------------- 1 | content addtotransmission content/ 2 | overlay chrome://browser/content/browser.xul chrome://addtotransmission/content/overlay.xul 3 | 4 | locale addtotransmission en-US locale/en-US/ 5 | locale addtotransmission sv-SE locale/sv-SE/ 6 | 7 | component {e2ad1660-1b52-11e4-8c21-0800200c9a66} components/magnetService.js 8 | contract @mozilla.org/network/protocol;1?name=magnet {e2ad1660-1b52-11e4-8c21-0800200c9a66} 9 | -------------------------------------------------------------------------------- /AddToTransmission-Firefox/locale/en-US/overlay.properties: -------------------------------------------------------------------------------- 1 | downloadingString=Downloading 2 | failedFetchString=Failed to fetch the torrent 3 | sendingString=Sending 4 | unauthorizedString=Unauthorized 5 | emptyResponseString=Empty response 6 | couldNotConnectString=Couldn't connect to Transmission 7 | invalidTorrentString=Invalid or corrupt torrent 8 | duplicateTorrentString=Duplicate torrent 9 | unknownErrorString=Unknown error 10 | successString=Added -------------------------------------------------------------------------------- /AddToTransmission-Firefox/defaults/preferences/pref.js: -------------------------------------------------------------------------------- 1 | pref("extensions.addtotransmission.addpaused", true); 2 | pref("extensions.addtotransmission.alllinks", false); 3 | pref("extensions.addtotransmission.username", ""); 4 | pref("extensions.addtotransmission.path", ""); 5 | pref("extensions.addtotransmission.url", "http://127.0.0.1:9091/transmission/rpc"); 6 | pref("extensions.addtotransmission.placement", "t"); 7 | pref("extensions.addtotransmission.troubleshooting", false); 8 | pref("extensions.addtotransmission.downloads", false); 9 | -------------------------------------------------------------------------------- /AddToTransmission-Firefox/locale/en-US/overlay.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /AddToTransmission-Firefox/content/overlay.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 67 | 68 | 71 | 72 | 75 | 76 | 79 | 80 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 123 | 124 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /AddToTransmission.safariextension/global.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /AddToTransmission-Firefox/content/overlay.js: -------------------------------------------------------------------------------- 1 | Components.utils.import("chrome://addtotransmission/content/torrentClient.js"); 2 | 3 | var uriContentListener = { 4 | QueryInterface: function(aIID) { 5 | if (aIID.equals(Components.interfaces.nsISupports) 6 | || aIID.equals(Components.interfaces.nsIURIContentListener) 7 | || aIID.equals(Components.interfaces.nsISupportsWeakReference) 8 | ) { 9 | return this; 10 | } 11 | return false; 12 | }, 13 | canHandleContent: function(contentType, isContentPreferred, desiredContentType) { 14 | return false; 15 | }, 16 | doContent: function(contentType, isContentPreferred, request, contentHandler) { 17 | var torrentInfo = {'href': request.URI.spec, 'id': null, 'magnet': false}; 18 | var newTorrent = new TorrentClient(torrentInfo, null); 19 | newTorrent.add(); 20 | return true; 21 | }, 22 | isPreferred: function(contentType, desiredContentType) { 23 | if (contentType == "application/x-bittorrent") { 24 | return true; 25 | } 26 | 27 | return false; 28 | }, 29 | onStartURIOpen: function(URI) { 30 | return true; 31 | } 32 | }; 33 | 34 | function TorrentClientDelegate() { 35 | this.showText = function(str, id, close) { 36 | var bubble = content.document.getElementById(id); 37 | if (bubble != null) { 38 | var bundles = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Components.interfaces.nsIStringBundleService); 39 | var strings = bundles.createBundle("chrome://addtotransmission/locale/overlay.properties"); 40 | bubble.textContent = strings.GetStringFromName(str); 41 | bubble.style.display = 'block'; 42 | bubble.style.opacity = 0.9; 43 | if (close) { 44 | // Fade out, set timeout to remove it 45 | setTimeout(function() { 46 | if (typeof(content.document.body) !== 'undefined') { 47 | try { 48 | content.document.getElementById(id).style.opacity = 0; 49 | } catch (e) { 50 | /*Do nothing*/ 51 | } 52 | } 53 | }, 1000); 54 | setTimeout(function() { 55 | if (typeof(content.document.body) !== 'undefined') { 56 | try { 57 | content.document.body.removeChild(content.document.getElementById(id)); 58 | } catch (e) { 59 | /*Do nothing*/ 60 | } 61 | } 62 | }, 5000); 63 | } 64 | } 65 | }; 66 | } 67 | 68 | var AddToTransmission = { 69 | session_id: false, 70 | userInfo: false, 71 | prefManager: Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch), 72 | lastClickEvent: null, 73 | launchListener: function() { 74 | if (this.prefManager.getBoolPref("extensions.addtotransmission.downloads")) { 75 | var uriLoader = Components.classes["@mozilla.org/uriloader;1"].getService(Components.interfaces.nsIURILoader); 76 | uriLoader.registerContentListener(uriContentListener); 77 | } 78 | }, 79 | onLoad: function(e) { 80 | var that = this; 81 | var contextMenu = document.getElementById("contentAreaContextMenu"); 82 | 83 | if (contextMenu) { 84 | window.addEventListener("contextmenu", function (e) { that.handleContextMenu(e); }, false); 85 | this.loadCSS(); 86 | } 87 | 88 | var anchors = content.document.getElementsByName("a"); 89 | console.log("brooo"); 90 | for (var i = 0; i < anchors.length; i++) { 91 | console.log("iaiia"); 92 | anchors[i].addEventListener("click", function(event) { 93 | console.log("douchebag"); 94 | that.lastClickEvent = event; 95 | }); 96 | } 97 | }, 98 | loadCSS: function() { 99 | var sss = Components.classes["@mozilla.org/content/style-sheet-service;1"].getService(Components.interfaces.nsIStyleSheetService); 100 | var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService); 101 | var uri = ios.newURI("chrome://addtotransmission/content/style.css", null, null); 102 | 103 | if (!sss.sheetRegistered(uri, sss.USER_SHEET)) { 104 | sss.loadAndRegisterSheet(uri, sss.USER_SHEET); 105 | } 106 | }, 107 | findParentNode: function(parentName, childObj) { 108 | var testObj = childObj.parentNode; 109 | var count = 1; 110 | while (testObj.nodeName != parentName && count < 50) { 111 | testObj = testObj.parentNode; 112 | if (testObj == null) { 113 | return false; 114 | } 115 | count++; 116 | } 117 | 118 | if (testObj.nodeName == "A") { 119 | return testObj.href; 120 | } else if (testObj.nodeName == "FORM") { 121 | return testObj.action; 122 | } else { 123 | return false; 124 | } 125 | }, 126 | handleContextMenu: function(event) { 127 | var menu = document.getElementById('addtotransmission-ctx'); 128 | 129 | var href = false; 130 | if (event.target.nodeName == "A") { 131 | href = event.target.href; 132 | } else if (event.target.nodeName == "INPUT" || event.target.nodeName == "BUTTON") { 133 | href = this.findParentNode("FORM", event.target); 134 | } else { 135 | href = this.findParentNode("A", event.target); 136 | } 137 | 138 | var allLinks = this.prefManager.getBoolPref("extensions.addtotransmission.alllinks"); 139 | 140 | if (href != false && (href.toLowerCase().indexOf('.torrent') != -1 || href.toLowerCase().indexOf('magnet:?') != -1 || allLinks)) { 141 | var magnet = (href.toLowerCase().indexOf('magnet:?') != -1 ? true : false); 142 | var d = new Date(); 143 | var id = d.getTime(); 144 | 145 | var placement = this.prefManager.getCharPref("extensions.addtotransmission.placement"); 146 | 147 | var bubble = content.document.createElement("div"); 148 | bubble.id = id; 149 | bubble.className = "att-bubble arrow"; 150 | bubble.style.display = 'none'; 151 | 152 | var pos = {}; 153 | switch (placement) { 154 | case 'b': 155 | pos = {top: event.pageY + 30, left: event.pageX - 70}; 156 | bubble.className += " bottom"; 157 | break 158 | case 't': 159 | pos = {top: event.pageY - 70, left: event.pageX - 70}; 160 | bubble.className += " top"; 161 | break 162 | case 'l': 163 | pos = {top: event.pageY - 22, right: (content.document.body.offsetWidth-event.pageX) + 70}; 164 | bubble.className += " left"; 165 | break 166 | case 'r': 167 | pos = {top: event.pageY - 22, left: event.pageX + 70}; 168 | bubble.className += " right"; 169 | break 170 | } 171 | 172 | if (pos.left) { 173 | bubble.style.left = pos.left+"px"; 174 | } else { 175 | bubble.style.right = pos.right+"px"; 176 | } 177 | bubble.style.top = pos.top+"px"; 178 | 179 | content.document.body.appendChild(bubble); 180 | 181 | menu.hidden = false; 182 | this.userInfo = {'href':href,'id':id,'magnet':magnet}; 183 | return; 184 | } 185 | menu.hidden = true; 186 | this.userInfo = false; 187 | }, 188 | contextMenuItemClicked: function(event) { 189 | if (typeof this.userInfo == 'boolean') { 190 | return; 191 | } 192 | 193 | var delegate = new TorrentClientDelegate(); 194 | var newTorrent = new TorrentClient(this.userInfo, delegate); 195 | newTorrent.add(); 196 | } 197 | }; 198 | 199 | window.addEventListener("load", function(e) { 200 | AddToTransmission.onLoad(e); 201 | }, false); -------------------------------------------------------------------------------- /AddToTransmission-Firefox/content/torrentClient.js: -------------------------------------------------------------------------------- 1 | var EXPORTED_SYMBOLS = ["TorrentClient"]; 2 | 3 | const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest"); 4 | 5 | /** 6 | * Transmission torrent client 7 | * @param info Torrent info 8 | * @param delegate Delegate for showing fancy text stuff 9 | * @constructor 10 | */ 11 | function TorrentClient(info, delegate) { 12 | this.info = info; 13 | this.delegate = delegate; 14 | this.prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); 15 | this.loginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager); 16 | this.keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/l"; 17 | this.session_id = null; 18 | this.add = function() { 19 | var that = this; 20 | // Add paused? defaults to YES! 21 | var addPaused = that.prefManager.getBoolPref("extensions.addtotransmission.addpaused"); 22 | var downloadDir = that.prefManager.getCharPref("extensions.addtotransmission.path"); 23 | 24 | if (that.info.magnet) { 25 | // Send the magnet link 26 | that.upload(JSON.stringify({method: "torrent-add", arguments: {filename: that.info.href, paused: addPaused, "download-dir": downloadDir}})); 27 | } else { 28 | // Try to download the torrent file 29 | that.showText('downloadingString'); 30 | try { 31 | var request = new XMLHttpRequest(); 32 | request.open("GET", that.info.href, true); 33 | request.overrideMimeType('text/plain; charset=x-user-defined'); 34 | request.onreadystatechange = function() { 35 | if (request.readyState == 4) { 36 | // Base64 encode the metadata 37 | var metainfo = that.encodeBinary(request.responseText); 38 | 39 | that.upload(JSON.stringify({method: "torrent-add", arguments: {metainfo: metainfo, paused: addPaused, "download-dir": downloadDir}})); 40 | } 41 | } 42 | 43 | request.send(null); 44 | } catch (e) { 45 | that.showText('failedFetchString', true); 46 | } 47 | } 48 | }; 49 | 50 | this.upload = function(data) { 51 | var that = this; 52 | 53 | that.showText('sendingString'); 54 | var troubleshooting = this.prefManager.getBoolPref("extensions.addtotransmission.troubleshooting"); 55 | var request = new XMLHttpRequest(); 56 | var username = that.prefManager.getCharPref("extensions.addtotransmission.username"); 57 | var url = that.prefManager.getCharPref("extensions.addtotransmission.url"); 58 | var password = false; 59 | var logins = that.loginManager.findLogins({}, "chrome://addtotransmission", url, null); 60 | for (var i=0; i< logins.length; i++) { 61 | if (logins[i].username == username) { 62 | password = logins[i].password; 63 | break; 64 | } 65 | } 66 | 67 | if (username.length > 0 && password != false) { 68 | request.open("POST", that.prefManager.getCharPref("extensions.addtotransmission.url"), true, username, password); 69 | } else { 70 | request.open("POST", that.prefManager.getCharPref("extensions.addtotransmission.url"), true); 71 | } 72 | 73 | if (that.session_id) { 74 | request.setRequestHeader("X-Transmission-Session-Id", that.session_id); 75 | } 76 | 77 | request.onreadystatechange = function() { 78 | if (request.readyState == 4) { 79 | var response = request.responseText; 80 | try { 81 | if (response.length > 0) { 82 | if (response.search(/invalid session-id/i) != -1) { 83 | var matches = response.match(/X-Transmission-Session-Id: (.*)<\/code>/); 84 | that.session_id = matches[1]; 85 | that.upload(data); 86 | } else if (response.search(/unauthorized/i) != -1) { 87 | that.showText('unauthorizedString', true); 88 | } else { 89 | var json_response = JSON.parse(response); 90 | if (json_response.result == 'success') { 91 | that.showText('successString', true); 92 | } else { 93 | if (!troubleshooting) { 94 | if (json_response.result == 'duplicate torrent') { 95 | that.showText('duplicateTorrentString', true); 96 | } else if (json_response.result == 'invalid or corrupt torrent file') { 97 | that.showText('invalidTorrentString', true); 98 | } else { 99 | that.showText('unknownErrorString', true); 100 | } 101 | } else { 102 | that.showText(json_response.result.charAt(0).toUpperCase() + json_response.result.slice(1), true); 103 | } 104 | } 105 | } 106 | } else { 107 | that.showText('couldNotConnectString', true); 108 | } 109 | } catch (e) { 110 | that.showText('unknownErrorString', true); 111 | } 112 | } 113 | }; 114 | 115 | request.send(data); 116 | }; 117 | this.showText = function(str, close) { 118 | if (!close) { 119 | close = false; 120 | } 121 | 122 | if (delegate == null) { 123 | return; 124 | } 125 | 126 | this.delegate.showText(str, this.info.id, close); 127 | }; 128 | 129 | // From http://emilsblog.lerch.org/2009/07/javascript-hacks-using-xhr-to-load.html 130 | this.encodeBinary = function(input) { 131 | var output = ""; 132 | var bytebuffer; 133 | var encodedCharIndexes = new Array(4); 134 | var inx = 0; 135 | var paddingBytes = 0; 136 | 137 | while(inx < input.length){ 138 | // Fill byte buffer array 139 | bytebuffer = new Array(3); 140 | for(var jnx = 0; jnx < bytebuffer.length; jnx++) 141 | if(inx < input.length) 142 | bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff; // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data 143 | else 144 | bytebuffer[jnx] = 0; 145 | 146 | // Get each encoded character, 6 bits at a time 147 | // index 1: first 6 bits 148 | encodedCharIndexes[0] = bytebuffer[0] >> 2; 149 | // index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2) 150 | encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4); 151 | // index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3) 152 | encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6); 153 | // index 3: forth 6 bits (6 least significant bits from input byte 3) 154 | encodedCharIndexes[3] = bytebuffer[2] & 0x3f; 155 | 156 | // Determine whether padding happened, and adjust accordingly 157 | paddingBytes = inx - (input.length - 1); 158 | switch(paddingBytes){ 159 | case 2: 160 | // Set last 2 characters to padding char 161 | encodedCharIndexes[3] = 64; 162 | encodedCharIndexes[2] = 64; 163 | break; 164 | case 1: 165 | // Set last character to padding char 166 | encodedCharIndexes[3] = 64; 167 | break; 168 | default: 169 | break; // No padding - proceed 170 | } 171 | // Now we will grab each appropriate character out of our keystring 172 | // based on our index array and append it to the output string 173 | for(var jnx = 0; jnx < encodedCharIndexes.length; jnx++) 174 | output += this.keyStr.charAt(encodedCharIndexes[jnx]); 175 | } 176 | return output; 177 | }; 178 | } --------------------------------------------------------------------------------