|
| 1 | +--[[ |
| 2 | + Steghide storage for darktable |
| 3 | +
|
| 4 | + copyright (c) 2016 Holger Klemm |
| 5 | + |
| 6 | + darktable is free software: you can redistribute it and/or modify |
| 7 | + it under the terms of the GNU General Public License as published by |
| 8 | + the Free Software Foundation, either version 3 of the License, or |
| 9 | + (at your option) any later version. |
| 10 | + |
| 11 | + darktable is distributed in the hope that it will be useful, |
| 12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | + GNU General Public License for more details. |
| 15 | + |
| 16 | + You should have received a copy of the GNU General Public License |
| 17 | + along with darktable. If not, see <http://www.gnu.org/licenses/>. |
| 18 | +]] |
| 19 | + |
| 20 | +--[[ |
| 21 | +Version 2.1 |
| 22 | +
|
| 23 | +
|
| 24 | +ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT |
| 25 | +* steghide |
| 26 | +
|
| 27 | +USAGE |
| 28 | +* require this file from your main luarc config file. |
| 29 | +
|
| 30 | +This plugin will add a new storage option and calls hugin after export. |
| 31 | +]] |
| 32 | + |
| 33 | +local dt = require "darktable" |
| 34 | +local gettext = dt.gettext |
| 35 | + |
| 36 | +-- works only with darktable API version 4.0.0 |
| 37 | +dt.configuration.check_version(...,{4,0,0}) |
| 38 | + |
| 39 | +-- Tell gettext where to find the .mo file translating messages for a particular domain |
| 40 | +gettext.bindtextdomain("steghideexport",dt.configuration.config_dir.."/lua/") |
| 41 | + |
| 42 | +local function _(msgid) |
| 43 | + return gettext.dgettext("steghideexport", msgid) |
| 44 | +end |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | + |
| 49 | +local check_button_selected_file = dt.new_widget("check_button") |
| 50 | +{ |
| 51 | + label = _('Use selected text file'), |
| 52 | + value = true, |
| 53 | + tooltip =_('Default text file: ~/.config/darktable/steghide/steghide_default'), |
| 54 | + reset_callback = function(self) |
| 55 | + self.value = true |
| 56 | + end |
| 57 | + |
| 58 | +} |
| 59 | + |
| 60 | + |
| 61 | +local check_button_usertext = dt.new_widget("check_button") |
| 62 | +{ |
| 63 | + label = _('Use user text line'), |
| 64 | + value = false, |
| 65 | + tooltip =_('Embed the user text line'), |
| 66 | + reset_callback = function(self) |
| 67 | + self.value = false |
| 68 | + end |
| 69 | + |
| 70 | +} |
| 71 | + |
| 72 | + |
| 73 | +local label_password = dt.new_widget("label") |
| 74 | +{ |
| 75 | + label = _('Password:'), |
| 76 | + ellipsize = "start", |
| 77 | + halign = "start", |
| 78 | + tooltip = _('Steghide requires a password') |
| 79 | +} |
| 80 | + |
| 81 | +local entrypassword = dt.new_widget("entry") |
| 82 | +{ |
| 83 | + text = "", |
| 84 | + placeholder = _('Please enter a password'), |
| 85 | + is_password = false, |
| 86 | + editable = true, |
| 87 | + tooltip = _('Enter a password'), |
| 88 | + reset_callback = function(self) |
| 89 | + self.text = "" |
| 90 | + end |
| 91 | +} |
| 92 | + |
| 93 | + |
| 94 | +local label_usertext= dt.new_widget("label") |
| 95 | +{ |
| 96 | + label = _('User text line:'), |
| 97 | + ellipsize = "start", |
| 98 | + halign = "start" |
| 99 | +} |
| 100 | + |
| 101 | + |
| 102 | +local entrytext = dt.new_widget("entry") |
| 103 | +{ |
| 104 | + text = "", |
| 105 | + sensitive = true, |
| 106 | + is_password = true, |
| 107 | + editable = true, |
| 108 | + tooltip = _('Enter the user text to embed'), |
| 109 | + reset_callback = function(self) |
| 110 | + self.text = "" |
| 111 | + end |
| 112 | + |
| 113 | +} |
| 114 | + |
| 115 | + |
| 116 | +local label_path = dt.new_widget("label") |
| 117 | +{ |
| 118 | + label = _('Target directory:'), |
| 119 | + ellipsize = "start", |
| 120 | + halign = "start" |
| 121 | +} |
| 122 | + |
| 123 | +local label_textfile= dt.new_widget("label") |
| 124 | +{ |
| 125 | + label = _('Selected text file:'), |
| 126 | + ellipsize = "start", |
| 127 | + halign = "start" |
| 128 | +} |
| 129 | + |
| 130 | +local label_enc= dt.new_widget("label") |
| 131 | +{ |
| 132 | + label = _('Encryption algorithm / mode:'), |
| 133 | + ellipsize = "start", |
| 134 | + halign = "start" |
| 135 | +} |
| 136 | + |
| 137 | + |
| 138 | +-- Target directory dialog |
| 139 | +local file_chooser_button = dt.new_widget("file_chooser_button") |
| 140 | +{ |
| 141 | + title = _('Export Steghide JPEG'), -- The title of the window when choosing a file |
| 142 | + is_directory = true -- True if the file chooser button only allows directories to be selecte |
| 143 | +} |
| 144 | + |
| 145 | + |
| 146 | +-- Text file dialog |
| 147 | +local textfile_chooser_button = dt.new_widget("file_chooser_button") |
| 148 | +{ |
| 149 | + title = _('Text file'), -- The title of the window when choosing a file |
| 150 | + value = ".config/darktable/steghide/steghide_default", |
| 151 | + reset_callback = function(self) |
| 152 | + self.value = ".config/darktable/steghide/steghide_default" |
| 153 | + end |
| 154 | +} |
| 155 | + |
| 156 | + |
| 157 | +local selection = dt.gui.selection() |
| 158 | + local result = "" |
| 159 | + local array = {} |
| 160 | + for _,img in pairs(selection) do |
| 161 | + array[img.path] = true |
| 162 | + end |
| 163 | + for path in pairs(array) do |
| 164 | + if result == "" then |
| 165 | + result = path |
| 166 | + else |
| 167 | + result = result.."\n"..path |
| 168 | + end |
| 169 | + end |
| 170 | + file_chooser_button.value = result |
| 171 | + |
| 172 | +local enc_combobox = dt.new_widget("combobox") |
| 173 | +{ |
| 174 | + label = "", |
| 175 | + value = 15, "cast-128 cbc", "cast-128 cfb", "cast-128 ctr","cast-128 ecb","cast-128 ncfb","cast-128 nofb","cast-128 ofb","gost cbc","gost cfb","gost ctr","gost ecb","gost ncfb","gost nofb","gost ofb","rijndael-128 cbc","rijndael-128 cfb","rijndael-128 ctr","rijndael-128 ecb","rijndael-128 ncfb","rijndael-128 nofb","rijndael-128 ofb","twofish cbc","twofish cfb","twofish ctr","twofish ecb","twofish ncfb","twofish nofb","twofish ofb","arcfour stream","cast-256 cbc","cast-256 cfb","cast-256 ctr","cast-256 ecb","cast-256 ncfb","cast-256 nofb","cast-256 ofb","loki97 cbc","loki97 cfb","loki97 ctr","loki97 ecb","loki97 ncfb","loki97 nofb","loki97 ofb","rijndael-192 cbc","rijndael-192 cfb","rijndael-192 ctr","rijndael-192 ecb","rijndael-192 ncfb","rijndael-192 nofb","rijndael-192 ofb","saferplus cbc","saferplus cfb","saferplus ctr","saferplus ecb","saferplus ncfb","saferplus nofb","saferplus ofb","wake stream","des cbc","des cfb","des ctr","des ecb","des ncfb","des nofb","des ofb","rijndael-256 cbc","rijndael-256 cfb","rijndael-256 ctr","rijndael-256 ecb","rijndael-256 ncfb","rijndael-256 nofb","rijndael-256 ofb","serpent cbc","serpent cfb","serpent ctr","serpent ecb","serpent ncfb","serpent nofb","serpent ofb","xtea cbc","xtea cfb","xtea ctr","xtea ecb","xtea ncfb","xtea nofb","xtea ofb","blowfish cbc","blowfish cfb","blowfish ctr","blowfish ecb","blowfish ncfb","blowfish nofb","blowfish ofb","enigma stream","rc2 cbc","rc2 cfb","rc2 ctr","rc2 ecb","rc2 ncfb","rc2 nofb","rc2 ofb","tripledes cbc","tripledes cfb","tripledes ctr","tripledes ecb","tripledes ncfb","tripledes nofb","tripledes ofb", |
| 176 | + reset_callback = function(self) |
| 177 | + self.value = 15 |
| 178 | + end |
| 179 | +} |
| 180 | + |
| 181 | + |
| 182 | +local widget = dt.new_widget("box") { |
| 183 | + orientation = "vertical", |
| 184 | + check_button_selected_file, |
| 185 | + check_button_usertext, |
| 186 | + label_textfile, |
| 187 | + textfile_chooser_button, |
| 188 | + label_usertext, |
| 189 | + entrytext, |
| 190 | + label_password, |
| 191 | + entrypassword, |
| 192 | + label_enc, |
| 193 | + enc_combobox, |
| 194 | + label_path, |
| 195 | + file_chooser_button, |
| 196 | +} |
| 197 | + |
| 198 | + |
| 199 | + |
| 200 | +local function checkIfBinExists(bin) |
| 201 | + local handle = io.popen("which "..bin) |
| 202 | + local result = handle:read() |
| 203 | + local ret |
| 204 | + handle:close() |
| 205 | + if (result) then |
| 206 | + -- dt.print_error("true checkIfBinExists: "..bin) |
| 207 | + ret = true |
| 208 | + else |
| 209 | + dt.print_error(bin.." not found") |
| 210 | + ret = false |
| 211 | + end |
| 212 | + |
| 213 | + |
| 214 | + return ret |
| 215 | +end |
| 216 | + |
| 217 | + |
| 218 | + |
| 219 | +local function show_status(storage, image, format, filename, |
| 220 | + number, total, high_quality, extra_data) |
| 221 | + if (not (entrypassword.text == "")) then |
| 222 | + dt.print(_('Export JPEG to Steghide ')..tostring(number).."/"..tostring(total)) |
| 223 | + else |
| 224 | + dt.print(_('ERROR: No password found')) |
| 225 | + end |
| 226 | +end |
| 227 | + |
| 228 | + |
| 229 | +encmode = enc_combobox.value |
| 230 | + |
| 231 | +local function create_steghidefoto(storage, image_table, extra_data) --finalize |
| 232 | + if (not (checkIfBinExists("steghide"))) then |
| 233 | + dt.print(_('ERROR: Steghide not found. Please install steghide.')) |
| 234 | + dt.print_error(_('Steghide not found. Please install steghide.')) |
| 235 | + return |
| 236 | + end |
| 237 | + |
| 238 | + local steghide_executor = false |
| 239 | + if (checkIfBinExists("steghide")) then |
| 240 | + steghide_executor = true |
| 241 | + end |
| 242 | + |
| 243 | + |
| 244 | +-- password check |
| 245 | +steghidepassword = entrypassword.text |
| 246 | +if steghidepassword =="" then |
| 247 | + dt.print(_('ERROR: No password found')) |
| 248 | +else |
| 249 | + dt.print(_('Will try to embed text')) |
| 250 | + |
| 251 | + -- textline check |
| 252 | + if ((check_button_usertext.value) and (entrytext.text == "")) then |
| 253 | + dt.print(_('ERROR: No user text found')) |
| 254 | + else |
| 255 | + |
| 256 | + |
| 257 | + -- create text file |
| 258 | + if ((check_button_selected_file.value) and (check_button_usertext.value)) then |
| 259 | + textline = entrytext.text |
| 260 | + default_file = io.open(textfile_chooser_button.value,"r") |
| 261 | + defaulttext = default_file:read("*a") |
| 262 | + combinetext = defaulttext.."\n\n"..textline |
| 263 | + combine_file = io.open("/tmp/steghide_combine", "w") |
| 264 | + combine_file:write(combinetext) |
| 265 | + io.close(combine_file) |
| 266 | + textfile = "/tmp/steghide_combine" |
| 267 | + elseif (check_button_selected_file.value) then |
| 268 | + textfile = textfile_chooser_button.value |
| 269 | + elseif (check_button_usertext.value) then |
| 270 | + textline_file = io.open("/tmp/steghide_text_line", "w") |
| 271 | + textline_file:write(entrytext.text) |
| 272 | + io.close(textline_file) |
| 273 | + textfile = "/tmp/steghide_text_line" |
| 274 | + elseif ((textfile =="") or (textfile=="(None)")) then |
| 275 | + dt.print(_('ERROR: No textfile created or selected')) |
| 276 | + return |
| 277 | + else |
| 278 | + dt.print(_('ERROR: No text file selected')) |
| 279 | + return |
| 280 | + end |
| 281 | + |
| 282 | + |
| 283 | + -- embed text with steghide |
| 284 | + local steghideStartCommand |
| 285 | + img_path = file_chooser_button.value |
| 286 | + if (steghide_executor) then |
| 287 | + if (textfile =="") then |
| 288 | + dt.print(_('ERROR: No textfile created or selected')) |
| 289 | + return |
| 290 | + else |
| 291 | + dt.print(_('Embeding text...')) |
| 292 | + for _,v in pairs(image_table) do |
| 293 | + steghideStartCommand = "steghide --embed --encryption " ..encmode.. " --nochecksum -q -p " ..steghidepassword.. " -cf "..v.." -ef "..textfile |
| 294 | + dt.control.execute( steghideStartCommand) |
| 295 | + moveFileCommand = "mv " ..v.. " "..img_path |
| 296 | + dt.control.execute( moveFileCommand) |
| 297 | + end |
| 298 | + end |
| 299 | + else |
| 300 | + dt.print(_('ERROR: Embeding not working')) |
| 301 | + end |
| 302 | + end |
| 303 | + |
| 304 | + end |
| 305 | + |
| 306 | +end |
| 307 | + |
| 308 | + |
| 309 | +-- limit export to jpeg (8 bit) |
| 310 | +local function support_format(storage, format) |
| 311 | + fmt = string.lower(format.name) |
| 312 | + if string.match(fmt,"jpeg%s%g8%sbit%g") == nil then |
| 313 | + return false |
| 314 | + else |
| 315 | + return true |
| 316 | + end |
| 317 | +end |
| 318 | + |
| 319 | + |
| 320 | + |
| 321 | +-- Register |
| 322 | +dt.register_storage("module_steghide", _('Steghide JPEG Photo'), show_status, create_steghidefoto, support_format, nil, widget) |
| 323 | + |
0 commit comments