ファイルをパスワード付きzipにしてDropboxのフォルダにコピーする

Dropboxってすばらしい。なんていまさら言うほどのことじゃないですね^^ だけど、ぼくにはちょっと気がかりなことがあります。もしDropboxが誰かに覗かれたりした時、ぼくのムフフな画像とか見られたらイヤじゃないですかー。...っていうか、そんなもんクラウドに上げてんじゃねえよ! 
まあ、ムフフでもムフフでなくても大切なファイルは気をつけたいものです。そもそも漏らしちゃいけないモノはクラウドに上げてはいけません。世界のソニーだってお漏らししちゃうくらいですから、安全なクラウドなんてないんですよね。安全神話なんて最初からないです。
ってことを念頭に置いた上で、Dropboxをすこしセキュアに使いたいって話です。セキュアって言っても大掛かりなものじゃなくて、パスワード付きzipをもう少し便利に使えたらいいな、と。Windows環境だと、標準でパスワード付きzipが作れます。しかし、Macだとターミナルからコマンドを打ち込まなくちゃいけません。もうちょっと簡単にドラッグ&ドロップだけパスワード付きzipを作りたいと思って去年書いたスクリプトがこちら:


で、Dropboxに馴れてくると、わざわざドロップレットにドラッグ&ドロップするのも面倒になってきます。ファイルの変更を当該マシンでしかやらないなら、定期的に自動でzipファイルを作ってDropboxのフォルダにコピーしてくれないかな... 人間ってどこまでも堕ちていくものですね。
ターゲットファイルとコピー先が1対1なので、簡単な感じのシェルスクリプトを書いてみます。

#! /bin/sh
cd /Volumes/myVolumeName/foo/ #カレントディレクトリを移動
zip -P xxxxxxxxxx hoge.zip hoge.txt #hoge.txtをパスワードxxxxxxxxxxでzip
mv -f hoge.zip ~/Dropbox/foo/var/hoge.zip #Dropboxの所定のフォルダへ移動

ちなみにファイルじゃなくてフォルダhogeをzipするときはこんな感じになります。

zip -rP xxxxxxxxxx hoge.zip hoge/  -x '*.DS_Store' '*__MACOSX/'

あとはcronで定時実行してやればいいのかな。cronについてはこちらを参考にしてください。


まだちょっと問題があります。cronで動かした時、ターゲットファイルが更新されていようといまいと、全部再処理されちゃうんですね。ひとつやふたつなら気にしませんが、たくさんあるともったいないオバケが出て来ちゃいます。ファイルの更新時間も見て、更新されたものだけ処理してくれたらいいな。ぼくがもう少しシェルスクリプトを書ける人だったらいいんですけれど、どうも苦手。
で、最初のドロップレットを少し改造するだけで、更新されたファイルだけ処理するようになります。アプリケーション形式で保存して、ログイン項目に追加したり、cronでopenしたりして使ってください。
最初の設定部分を適宜書き換えて使ってください。設定my_filesが多重リストになっているのが注意。

(*
copy_to_DropboxFolder_with_password
指定ファイル(またはフォルダ)のパスワード付きzipを生成してDropboxの所定のフォルダにコピーする。
ログイン項目に入れるとか、crontabで定期実行する。
ターゲットファイルの更新日を記憶し、ファイルが新しくなった時だけコピーを実行する。ただし、初回起動は常にコピー。
パスワードの設定を書き込まなければ、起動ごとにパスワードを入力するようになる。生成パスワードはクリップボードに保存
日本語ファイル名とか、記号類が使ってあるファイル名はサポートしない。いつものことだけど。
zipフォルダの伸長はunzipコマンドを使うのが吉。

for Mac OS X 10.7.2

2012-1-11	ver0.1	とりあえず

(c)2012 seuzo
*)

--======================設定
--パスワードの設定。空なら毎回パスワードを入力する
set my_password to "xxxxxxxxxxxxx"

--ターゲットファイルと保存フォルダのリスト。{{"ターゲットファイル", "保存フォルダ", ファイル更新日}, {"ターゲットファイル", "保存フォルダ", ファイル更新日}, ...}形式の多重リスト。ファイル更新日は初回起動時に自動的に追加される
property my_files : {{"/Volumes/myVolumeName/foo/hoge.txt", "~/Dropbox/foo/var/"}, {"~/foo/var/", "~/Dropbox/foo/var/"}}
--======================

--パスワードが空ならダイアログを出す
if my_password is "" then
	set my_password to my get_password()
	set the clipboard to my_password --クリップボードにパスワードを保存
end if

set my_files to my first_run(my_files) --初回起動時にファイル更新日を追加

--各ファイル処理
repeat with i in my_files
	set target_file to item 1 of i
	set copy_folder to item 2 of i
	set modification_date to item 3 of i
	--ターゲットファイルが新しいならzip&copyを実行
	if (my get_modification_date(target_file) > modification_date) then
		set item 3 of i to my get_modification_date(target_file) --書き換え
		my zip_and_copy(target_file, copy_folder, my_password) --zip & copy
	end if
end repeat





--パスワードを生成
to generate_password(n)
	set pass to ""
	set str to "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	repeat n times
		set pass to pass & (some character of str)
	end repeat
	return pass
end generate_password


--パスワードを得る
to get_password()
	set my_password to (text returned of (display dialog "Input Zip Password" & return & "(空白にすれば、パスワードを自動生成します)" & return & "終了後、パスワードはクリップボードに保存されます" default answer "" default button 2 with hidden answer))
	if (my_password is "") then
		set my_password to my generate_password(10)
	else
		set password_test to do shell script "echo " & my_password & " | perl -pe 's/[0-9A-Za-z]+//;'"
		if (password_test is not "") then
			display dialog "パスワードに半角英数字以外の文字が使われています。" buttons {"Cancel"} default button 1 with icon 1 --キャンセルで終了
		end if
	end if
	return my_password
end get_password


--初回起動時にファイル更新日を追加
to first_run(tmp_files)
	if (length of item 1 of tmp_files is 2) then
		tell application "Finder"
			repeat with i from 1 to (length of tmp_files)
				set f to item 1 of item i of tmp_files
				set f to (f as POSIX file) as alias
				set item i of tmp_files to item i of tmp_files & ((modification date of f) - 60) --1分だけ過去を設定
			end repeat
		end tell
	end if
	return tmp_files
end first_run

--POSIX形式からファイル更新日を得る
to get_modification_date(f)
	tell application "Finder"
		set f to (f as POSIX file) as alias
		return modification date of f
	end tell
end get_modification_date


--同名ファイルの警告
to alert_same_name(file_path)
	tell application "Finder"
		if (exists (file_path as POSIX file)) then
			display dialog file_path & " は同名のファイルがあります" & return & "上書きしますか?" buttons {"Cancel", "上書き"} default button 1 with icon 1 --キャンセルで終了
		end if
	end tell
end alert_same_name


--ファイル名の警告
to violation_name(file_path)
	set file_path to quoted form of ((POSIX path of file_path) as Unicode text) --まずPOSIX pathにする
	set file_path_test to do shell script "echo " & file_path & " | perl -pe 's/[\\/\\.0-9A-Za-z _-]+//;'"
	if (file_path_test is not "") then
		display dialog file_path & " のファイル名には英数字以外の文字が使われています。" & return & "圧縮・送信・伸長時にトラブルになるかもしれません。続行しますか?" buttons {"Cancel", "続行"} default button 1 with icon 1 --キャンセルで終了
	end if
end violation_name

--フルパス名を、コンテナディレクトリ/ファイル名+拡張子/ファイル名/拡張子に分割する
--引数fはファイルの参照。alias/POSIX path(文字列)/HFS path(文字列)
--引数path_typeは返値のパス形式。"alias"/"POSIX_path"/"HFS_path"
--返値はレコード {full_path:フルパス dir:コンテナディレクトリ名, name_ext:ファイル名(拡張子付き), f_name:ファイル名(拡張子なし), dot_ext:拡張子(.ドット付き), ext:拡張子(.ドットなし)}
to split_path(f, path_type)
	try
		set f to (POSIX path of f) as Unicode text
		set f to (f as POSIX file) as alias
	on error errMsg number errNo
		display dialog "Path変換エラーです" & return & errMsg & return & errNo buttons {"Cancel"} default button 1 with icon 1 --キャンセルで終了
	end try
	
	tell application "Finder"
		--fがボリュームだった
		if (kind of f) is "ボリューム" then
			set ne to name of f --ファイル名+拡張子
			if path_type is "POSIX_path" then
				set f to POSIX path of f
			else if path_type is "HFS_path" then
				set f to f as Unicode text
			end if
			return {full_path:f, dir:"", name_ext:ne, f_name:ne, dot_ext:"", ext:""}
		end if
		
		--fはファイルまたはフォルダ
		set d to (container of f) as alias --コンテナディレクトリ
		set ne to name of f --ファイル名+拡張子
		set e to name extension of f --拡張子(.なし)
		if e is "" then
			set n to ne --ファイル名(拡張子なし)
			set de to ""
		else
			set n to text 1 thru (-2 - (length of e)) of ne --拡張子を削除
			set de to "." & e --拡張子(.付き)
		end if
		
		--出力するpathのタイプ
		if path_type is "POSIX_path" then
			if (kind of f) is "フォルダ" then set ne to ne & "/" --フォルダなら最後に/をつける
			set f to POSIX path of f
			set d to POSIX path of d
		else if path_type is "HFS_path" then
			if (kind of f) is "フォルダ" then set ne to ne & ":" --フォルダなら最後に:をつける
			set f to f as Unicode text
			set d to d as Unicode text
		else if path_type is "alias" then
			--set f to f as alias
			--set d to d as alias
		end if
		
		return {full_path:f, dir:d, name_ext:ne, f_name:n, dot_ext:de, ext:e}
	end tell
end split_path

--リストの結合
to as_join(thedelimit, theList)
	set oldDelim to AppleScript's text item delimiters
	set AppleScript's text item delimiters to thedelimit
	set tmpstr to theList as text
	set AppleScript's text item delimiters to oldDelim
	return tmpstr
end as_join



--パスワード付きzipとコピー
to zip_and_copy(target_file, copy_folder, my_password)
	my violation_name(target_file) --ファイル名の警告
	set file_path to my split_path(target_file, "POSIX_path") --ファイルパスの分割
	set file_path_zip to quoted form of (f_name of file_path & ".zip") --.zip拡張子
	set file_path_org to quoted form of name_ext of file_path --同じディレクトリに展開するので、ファイル名のみ
	
	try
		set my_script to "cd " & dir of file_path & "; zip -rP " & my_password & " " & file_path_zip & " " & file_path_org & " -x '*.DS_Store' '*__MACOSX/' ; mv -f " & file_path_zip & " " & copy_folder & (f_name of file_path & ".zip")
		--display dialog my_script
		
		set add_files to do shell script my_script
		--display dialog add_files
	on error errMsg number errNo
		display dialog "zip実行エラーです" & return & errMsg & return & errNo buttons {"Cancel"} default button 1 with icon 1 --キャンセルで終了
	end try
	
end zip_and_copy