// V2C Sync2ch Script //【登録場所】 全体 //【ラベル】 Sync2ch同期 //【内容】 sync2ch.com WEBサービスのAPIを利用し、タブ一覧とお気に入り一覧を // サーバーのデーターと同期します。 //【コマンド1】${SCRIPT:A} v2c_sync.txt (Sync2ch同期) //【コマンド2】${SCRIPT:A} v2c_sync.txt option (同期設定) //【コマンド3】${SCRIPT:A} v2c_sync.txt auto (自動同期 ON/OFF) //【コマンド4】${SCRIPT:A} v2c_sync.txt autoStart (起動時自動同期) //StartUpフォルダへ登録 //【コマンド5】${SCRIPT:A} v2c_sync.txt readAll (全タブ既読化) var CLIENT_NAME = "V2C Sync"; var CLIENT_VERSION = "3.1.2"; var CONF_FILENAME = "conf.txt"; //設定ファイル名 var CLIENT_ID_FILENAME = "client_id.txt" var COUNT_DATA_FILENAME = "data.txt"; var POST_DATA_FILENAME = "posts.txt"; var AUTO_PROPERTY_NAME = "key_auto_session"; var MIN_SYNC_SPAN_MINUTES = 3;//自動同期間隔制限 var DEFAULT_SYNC_SPAN_MINUTES = 10;//デフォルト自動同期間隔 var DEBUG = false; var SAVE_LOG = false; var USESSL = true; var ARG_OPTION = 'option'; //設定画面起動 var ARG_UNREAD_ALL = 'readAll';//全タブ既読化 var ARG_TOGGLE_AUTO = 'auto'; //自動同期トグル var ARG_AUTOSTART = 'autoStart'; //自動同期開始 var ARG_DEBUG = 'debug'; var ARG_LOG = "log"; var ARG_NOSSL = "nossl"; function main() { DEBUG = hasArg( ARG_DEBUG ); SAVE_LOG = hasArg( ARG_LOG ); USESSL = !hasArg ( ARG_NOSSL ); if (hasArg( ARG_UNREAD_ALL )) { markAllTabsAsRead(); return; } if (hasArg( ARG_AUTOSTART )) { startAutoSync(); return; } if (hasArg( ARG_TOGGLE_AUTO ) ) { if (v2c.getProperty(AUTO_PROPERTY_NAME)) { v2c.removeProperty(AUTO_PROPERTY_NAME); v2c.println("自動同期ストップリクエスト"); v2c.context.setStatusBarText("自動同期終了"); } else { startAutoSync(); } return; } if (hasArg( ARG_OPTION )) { startConfigure(); return; } trySync(true); } function hasArg(arg) { var args = v2c.context.args; for (var i = 0; i < args.length; i++) { if (args[i] == arg) { return true; } } return false; } function debug(str) { if (DEBUG) v2c.println(str); } function startAutoSync() { var thisSessionKey = "SK"+Math.random(); //多重起動防止用セッションキー v2c.putProperty(AUTO_PROPERTY_NAME, thisSessionKey); while(true) { var currentSession = v2c.getProperty(AUTO_PROPERTY_NAME); if (!currentSession || currentSession != thisSessionKey) { v2c.setStatus("自動同期終了"); v2c.context.setStatusBarText("自動同期終了"); v2c.println("自動同期終了"); return; } v2c.setStatus("自動同期開始"); v2c.println("自動同期開始"); var conf = getConfObject(); threadRepository.clear(); try { doSync(conf); } catch (e) { var message = "■エラー(自動同期中) " + e.lineNumber+ "行目: "+ e.toString(); v2c.println(message); v2c.context.setStatusBarText(message); } var span = new Number(conf.span); if (isNaN(span) || MIN_SYNC_SPAN_MINUTES > span) span = MIN_SYNC_SPAN_MINUTES;//最低3分 java.lang.Thread.sleep(span*60000);//60秒*1000ms } } function markAllTabsAsRead() { var all = getAllTabThreads(); for (var i = 0; i < all.length; i++) { var th = all[i]; th.resetUnread(); th.clearNewMark() } } function getAllTabThreads () { var threads = []; for(var colIndex = 0; colIndex < v2c.resPane.columnCount; colIndex++) { var column = v2c.resPane.columns[colIndex]; for(var tabIndex = column.tabCount - 1; tabIndex >= 0; tabIndex--) { var th = column.threads[tabIndex]; if (th != null) { threads.push(th); } } } return threads; } //設定ファイルを読み取り、同期を試みます。 function trySync(showOptionScreenIfFailed, conf) { if (conf == null) conf = getConfObject(); if (conf && conf.id && conf.password) { doSync(conf); //同期を開始 } else if (showOptionScreenIfFailed) { startConfigure(); } } function doSync(conf) { var id = conf.id; var pass = conf.password; localCountData.open(); postData.open(); //同期番号の読み込み var syncNumber = 0; var syncNumFile = v2c.getScriptDataFile("sync_number.txt"); if (syncNumFile.exists()) { try { var str = v2c.readFile(syncNumFile); syncNumber = java.lang.Integer.parseInt(str); v2c.println("同期番号(送信):"+syncNumber); } catch(e) { v2c.println("有効な同期番号を読み取れません。0で初期化します。"); } } //クライアント番号の読み込み var clientId = 0; var clientIdFile = v2c.getScriptDataFile(clientIdFile); if (clientIdFile.exists()) { try { var str = v2c.readFile(clientIdFile); clientId = java.lang.Integer.parseInt(str); } catch(e) { v2c.println("有効なクライアント番号を読み取れません。0で初期化します。"); } } var url = new java.net.URL( (USESSL?"https":"http")+"://"+getHost()+"/api/sync3"); var conn = url.openConnection(); conn.setDoOutput(true);// POST var useCompression = true; if (useCompression) conn.setRequestProperty("Encoding", "gzip"); conn.setRequestProperty("Accept-Encoding", "gzip"); conn.setRequestProperty("Authorization", "Basic " + Base64.encode(id+":"+pass)); conn.setRequestProperty("User-Agent", CLIENT_NAME +" "+ CLIENT_VERSION); var os = conn.getOutputStream(); if (useCompression) os = new java.util.zip.GZIPOutputStream(os); //var writer = ; var bw = new java.io.BufferedWriter(new java.io.OutputStreamWriter(os, "utf-8")); Crypt.setKey(conf.cryptLevel, conf.cryptPass); // リクエストXMLの作成 var xml = createRequestXml(syncNumber, clientId, conf); if (SAVE_LOG) saveSentXML(xml); debug(xml); bw.write(new java.lang.String(xml)); bw.close(); // レスポンスXML受信 var inputStream = conn.getInputStream(); var encHeader = conn.getHeaderField("Content-Encoding"); if (encHeader && encHeader+"" == "gzip") { inputStream = new java.util.zip.GZIPInputStream(inputStream); } //DOM var factory = javax.xml.parsers.DocumentBuilderFactory.newInstance(); var builder = factory.newDocumentBuilder(); var doc = builder.parse(inputStream); inputStream.close(); conn.disconnect(); var clientId = attr(doc.documentElement, "client_id"); if (clientId != null) { v2c.writeStringToFile(clientIdFile, clientId+""); } var recievedSyncNumber = attr(doc.documentElement, "sync_number"); var remainCount = attr(doc.documentElement, "remain"); var remainStr = remainCount == "-1" ?"": " | 残り同期回数:"+remainCount+ "回"; var accountType = attr(doc.documentElement, "account_type"); if (recievedSyncNumber) { var message = "同期完了(#"+ recievedSyncNumber +" | "+accountType+remainStr+") ["+ new Date().toLocaleTimeString()+"]"; //解析Main parseResponseDocument(conf, doc, function(){ v2c.writeStringToFile(syncNumFile, recievedSyncNumber+""); v2c.println("同期番号(受信):"+recievedSyncNumber); v2c.println(message); v2c.setStatus(message); v2c.context.setStatusBarText(message); }); } threadRepository.clear(); } function saveSentXML(xml) { try { var logFile = v2c.getScriptDataFile("sent_xml_log.txt"); var str = logFile.exists() ? v2c.readFile(logFile)+"" : ""; if (str.length > 500000) { str = str.substring(0, 470000); } var dateStr = "\n["+ new Date().toLocaleTimeString()+"]"; str = dateStr + "\n" + xml +"\n"+ str + "\n"; v2c.writeStringToFile(logFile, str); } catch(e) { v2c.println("ログ保存エラー"); } } function getHost() { var args = v2c.context.args; for (var i = 0; i < args.length; i++) { var arg = args[i]; if (arg.length() > 5 && arg.substring(0,5) == "host:") { return arg.substring(5); } } return "sync2ch.com"; } function canSyncThread(th) { //同期できるスレッドかどうか return th && !th.local && !th.bbs.twitter; } //newMarkResIndexとviewResIndexの初期化のためタブを開いていく function initAllTabThreads () { var first = true; for(var colIndex = 0; colIndex < v2c.resPane.columnCount; colIndex++) { var column = v2c.resPane.columns[colIndex]; var selectedThread = column.selectedThread ; for(var tabIndex = column.tabCount - 1; tabIndex >= 0; tabIndex--) { var th = column.threads[tabIndex]; if (th != null) { if (0 == th.viewResIndex) { column.openThread(th, false, false, false); if (first == true) { java.lang.Thread.sleep(600); first = false; } } } } if (selectedThread) { column.openThread(selectedThread, false, false, false); } } } function createRequestXml(syncNumber, clientId, conf) { var os = escapeXML(java.lang.System.getProperty("os.name")); var syncPosts = conf.post ? " sync_rl=\"post\"": ""; var cryptLevel = conf.cryptLevel > 0 ? " crypt_level=\""+conf.cryptLevel+"\"" : ""; var xml = "\n"; xml += "\n"; var entityIndex = 0; var idToThreads = {}; // id -> th var idToBoards = {}; var urlToIds = {}; //url -> id var initFinished = "InitFinished"; var shouldInit = "true" != v2c.getProperty(initFinished); if (shouldInit) { initAllTabThreads(); v2c.putProperty(initFinished,"true"); } //タブ一覧 if (conf.tab) { var column = getSyncTabColumn(conf); if (column) { var idList = []; for(var tabIndex = 0; tabIndex < column.tabCount; tabIndex++) { var th = column.getThread(tabIndex); if (canSyncThread(th)) { threadRepository.putIfMissing(th); var id = urlToIds[th.url]; if (id == undefined) { id = urlToIds[th.url] = entityIndex; idToThreads[id] = th; entityIndex++; } idList.push(id); } } xml += "\n"; } } // お気に入り一覧 if (conf.fav) { var hasDirChild = function(item) { for(var i = 0; i < item.childCount; i++) { var child = item.getChild(i); if (!child.thread && !child.board && child.label) { return true; } } return false; }; var addChilds = function(tagName, attrArray, folder) { var useIdList = !hasDirChild(folder); var innerXML = ""; var idList = useIdList ? [] : null; for (var i = 0; i < folder.childCount; i++) { var child = folder.getChild(i); if (child.thread) { var th = child.thread; threadRepository.putIfMissing(th); if (canSyncThread(th)) { var id = urlToIds[th.url]; if (id == undefined) { id = urlToIds[th.url] = entityIndex; idToThreads[entityIndex] = th; entityIndex++; } if (useIdList) { idList.push(id); } else { innerXML += "\n"; } } } else if (child.board) { var id = urlToIds[child.board.url]; if (id == undefined) { id = urlToIds[child.board.url] = entityIndex; idToBoards[entityIndex] = child.board; entityIndex++; } if (useIdList) { idList.push(id); } else { innerXML += "\n"; } } else if (child.label) { innerXML += addChilds('dir', {'name':Crypt.encFolder(child.label)}, child); } } if (useIdList) { attrArray['id_list'] = idList.join(','); return makeElement(tagName, attrArray, null); } else { return makeElement(tagName, attrArray, innerXML); } }; var targetFavTab = getFavTabForSync(conf); if (targetFavTab) { var favCate = "favorite"; var favStructId = "default"; xml += addChilds("thread_group", {'struct': favStructId, 'abbr':'true', 'category':favCate}, targetFavTab.root); } } xml += "\n"; for(var id in idToThreads) { xml += threadToXml(conf, idToThreads[id], id); } for (var id in idToBoards) { var bd = idToBoards[id]; var url = fixBoardUrl(bd.url+""); xml += ""; } xml += "\n"; xml += ""; return xml; } function escapeXML(str) { str = str + ''; return str.replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function makeElement(tagName, attrArray, innerXML) { if (innerXML) { var xml = "<" + tagName + arrayToAttributes(attrArray) +">\n"; xml += innerXML; xml += "\n"; return xml; } else { return "<" + tagName + arrayToAttributes(attrArray) +"/>\n"; } } //配列からXMLの属性表現を生成 function arrayToAttributes(assocArray) { var result = ""; for (var i in assocArray) { result += " " + i + "=\"" + assocArray[i] + "\""; } return result; } function threadToXml(conf, th, id) { var attrs = []; attrs['title'] = Crypt.encTitle(th.title, th.url); attrs['url'] = Crypt.encUrl(th.url); attrs['id'] = id; var resCount = th.localResCount > th.resCount ? th.localResCount : th.resCount; if (resCount > 1) { attrs['count'] = Crypt.encCount(resCount, th.url); if (localCountData.getCount(th.url)) { //dat落ち対策 if (localCountData.getCount(th.url) > resCount) { attrs['count'] = Crypt.encCount(localCountData.getCount(th.url), th.url); } } if (th.unread) { //未読時には現在位置まで既読済みにする。 if (th.viewResIndex > th.newMarkResIndex) { th.newMarkResIndex = th.viewResIndex + 1; } attrs['read'] = Crypt.encRead(th.newMarkResIndex, th.url); } else { if (th.localResCount && th.resCount && th.localResCount < th.resCount) { //未取得のレスがある場合 attrs['read'] = Crypt.encRead(th.localResCount, th.url); } else { attrs['read'] = Crypt.encRead(resCount, th.url); } } attrs['now'] = Crypt.encNow(1 + th.viewResIndex, th.url); } if (conf.post) { var posts = []; for (var i = 0; i < th.postResIndex.length; i++) { var idx = 1+Number(th.postResIndex[i]); posts.push(idx); } var reservedPosts = postData.getPosts(th.url); if (reservedPosts) { for(var i = 0; i < reservedPosts.length; i++) { posts.push(reservedPosts[i]); } } if (posts.length > 0) { attrs['rl_post'] = Crypt.encPostNums(posts, th.url);//.join(','); //debug(attrs['rl_post']); } } return "\t\n"; } //属性がある場合のみ、JavaScriptの文字列にして返す。 function attr(el, name) { if (!el || !name) return undefined; var attrNode = el.attributes.getNamedItem(name); if (attrNode) { return "" + attrNode.nodeValue; } return undefined; } function ThreadInfo () { //this.thread = null; } ThreadInfo.fromElement = function(node) { var info = new ThreadInfo(); info.isBoard = node.nodeName+"" == "bd"; info.url = Crypt.decUrl(attr(node, 'url')); info.title = Crypt.decTitle(attr(node, 'title'), info.url); info.count = Crypt.decCount(attr(node, 'count'), info.url); info.status = attr(node, 's'); info.readPos = Crypt.decRead(attr(node, 'read'), info.url); info.readingPos = Crypt.decNow(attr(node, 'now'), info.url); info.id = attr(node, 'id'); var posts = attr(node, "rl_post"); if (posts != undefined) { var tempPosts = (posts == "") ? [] : Crypt.decPostNums(posts, info.url); info.posts = []; for (var i in tempPosts) { info.posts.push(tempPosts[i]); } } return info; } var threadRepository = { map : {} ,clear : function() { delete this.map; this.map = {}; } ,put : function (url, th) { this.map[url] = th; } ,get : function (url) { return this.map[url]; } ,contains: function(url) { return this.map[url] !== undefined; } ,putIfMissing : function(th) { this.map[th.url] = th; } }; var updateManager = { updateThreadUrls : {} ,selectedThread : null , allThreads : {} , column: null , allTabThreads: null , init : function(conf) { this.column = getSyncTabColumn(conf); delete this.allThreads; this.allThreads = {}; } , saveSelectedThread: function() { this.selectedThread = this.column.selectedThread; } , restoreSelectedThread : function() { if (this.selectedThread) { this.column.openThread(this.selectedThread, false, false, false); } } , notifyRemoveSelectedThread: function(th) { if (this.selectedThread && this.selectedThread.url+"" == th.url+"") { this.selectedThread = null; } } , addThread : function(th, threadInfo) { if (threadInfo.status == 'n') { return; } if (!this.allThreads[th.url]) { threadInfo.thread = th; this.allThreads[th.url] = threadInfo; } } , applyPosts : function (th, threadInfo) { if (threadInfo.posts) { var shouldAdd = threadInfo.status == 'a'; var reservePosts = []; //予備データ保存用 debug("posts = "+ threadInfo.title + ": " + threadInfo.posts.join(",")); for (var i = 0; i < threadInfo.posts.length; i++) { try { var res = th.getRes(threadInfo.posts[i]-1); } catch(e) { v2c.println("error in th.getRes" + (threadInfo.posts[i])); } if (res && th.postResLabel) { res.setResLabel(th.postResLabel); } else { reservePosts.push(threadInfo.posts[i]); } } if (reservePosts.length > 0) { postData.setPosts(th.url, th.title, reservePosts); } else if (!shouldAdd) { postData.deletePosts(th.url); } if (!shouldAdd) { //消去 var removeResList = []; for (var i = 0; i < th.postResIndex.length; i++) { var resNumber = 1 + Number(th.postResIndex[i]); if (threadInfo.posts.indexOf(''+resNumber) == -1) { var res = th.getRes(th.postResIndex[i]); if (res) { removeResList.push(res); } } } for (var i in removeResList) { removeResList[i].setResLabel(null); } } } } , isThOpenInTab : function (th) { for (var i = 0; i < this.allTabThreads.length; i++) { var tabTh = this.allTabThreads[i]; if (th == tabTh || th.url+"" == tabTh.url+"") { return true; } } return false; } , updateThreads : function (conf) { this.allTabThreads = getAllTabThreads(); //既読化 for(var url in this.allThreads) { var threadInfo = this.allThreads[url]; var th = threadInfo.thread; var localResCount = th.localResCount; var readingPos = Number(threadInfo.readingPos); if (threadInfo.readPos > 0 && th.newMarkResIndex != threadInfo.readPos) { th.newMarkResIndex = threadInfo.readPos; if (localResCount <= threadInfo.readPos) { localCountData.deleteCount(th.url); if (th.unread) { th.clearNewMark(); } } } } var lazyUpdateThreads = []; //更新・ for(var url in this.allThreads) { var threadInfo = this.allThreads[url]; var th = threadInfo.thread; var localResCount = th.localResCount; var readingPos = Number(threadInfo.readingPos); var tempOpenMode = (this.isThOpenInTab(th) == false); var isOpenTweakThread = false; if (localResCount < threadInfo.readPos || localResCount < threadInfo.count) { //未取得レスが想定されるので更新。 if (th.unread) { localCountData.addThreadUrl(th.url, threadInfo.count); } var success = th.updateAndWait(); if (success) { localCountData.deleteCount(th.url); //更新に成功したらカウント消去 } if (tempOpenMode == false) { th.newMarkResIndex = threadInfo.readPos; if (th.localResCount <= threadInfo.readPos) { th.clearNewMark(); } } else { isOpenTweakThread = true; } } this.applyPosts(th, threadInfo); if ( readingPos > 0 && th.viewResIndex != readingPos - 1) { if (tempOpenMode) { isOpenTweakThread = true; } else { try { //v2c.println(th.viewResIndex+"[]:"+(readingPos-1)); th.viewResIndex = readingPos - 1; } catch(ex) { v2c.println("error occured while setting th.viewResIndex"); } } } if (isOpenTweakThread) { lazyUpdateThreads.push(threadInfo); } } //お気に入りのスレッドを開きながら既読・現在位置変更 for (var i in lazyUpdateThreads) { var threadInfo = lazyUpdateThreads[i]; var th = threadInfo.thread; var localResCount = th.localResCount; var readingPos = Number(threadInfo.readingPos); //th = v2c.getThread(th.url+"", threadInfo.title); this.column.openThread(th, false, true, true); //java.lang.Thread.sleep(300); //th = v2c.getThread(th.url+"", threadInfo.title); //java.lang.Thread.sleep(300); th.newMarkResIndex = threadInfo.readPos; if (th.localResCount <= threadInfo.readPos) { th.clearNewMark(); } //v2c.println(th.viewResIndex+":"+readingPos); if (th.unread == false) { //read == true th.close(); this.column.openThread(th, false, true, false); java.lang.Thread.sleep(100); } if (readingPos > 0 && th.viewResIndex != readingPos - 1) { try { th.viewResIndex = readingPos - 1; } catch(ex) { v2c.println("error occured while setting th.viewResIndex"); } } java.lang.Thread.sleep(200); if (!(conf.remainTab && threadInfo.status =='a')) { th.close(); } } localCountData.deleteMissingThreads(this.allThreads); localCountData.save(); postData.save(); } }; //書き込みラベルを適切に設定できなかった時のためのデータ var postData = { // "http://dummy.2ch.net/test/read.cgi/bar/132112438/" : { "title": "Thread Title", "posts" : [313, 532] } map : {} ,open : function() { var postFile = v2c.getScriptDataFile(POST_DATA_FILENAME); if (postFile.exists()) { try { var dataText = v2c.readFile(postFile); this.map = eval(dataText+""); if (this.map == null) { //v2c.println(dataText); this.map = {}; } } catch (e) { if (this.map == null) { this.map = {}; } v2c.println(POST_DATA_FILENAME+ "読み込みエラー"); } } } ,save : function() { var dataFile = v2c.getScriptDataFile(POST_DATA_FILENAME); var str = ""; for(var url in this.map) { var entry = this.map[url]; if (entry["posts"] && entry["posts"].length > 0) { var joined = entry["posts"].join(); if (joined != '') { str += '"' + url +'":{"title":"'+entry.title+'", "posts":['+joined+"]},\n\t"; } } } v2c.writeStringToFile(dataFile, "({\n\t"+str+"\n})"); } ,getPosts : function (url) { return this.map[url] ? this.map[url]["posts"] : null; } ,setPosts: function (url, title, posts) { this.map[url] = {"title": title, "posts" : posts}; } ,deletePosts : function (url, posts) { delete this.map[url]; } }; //Dat落ち対策のためのカウント数保持 var localCountData = { map : {} ,open : function () { var dataFile = v2c.getScriptDataFile(COUNT_DATA_FILENAME); if (dataFile.exists()) { try { var dataText = v2c.readFile(dataFile); this.map = eval(dataText+""); if (this.map == null) { //v2c.println(dataText); this.map = {}; } }catch(e) { if (this.map == null) { this.map = {}; } v2c.println(COUNT_DATA_FILENAME+ "読み込みエラー"); } } } ,save : function () { var dataFile = v2c.getScriptDataFile(COUNT_DATA_FILENAME); var str = ""; for(var i in this.map) { str += '"' + i +'":'+this.map[i]+",\n\t"; } v2c.writeStringToFile(dataFile, "({\n\t"+str+"\n})"); } ,addThreadUrl : function(url, count) { this.map[url] = count; } ,getCount : function(url) { return this.map[url]; } ,deleteCount : function(url) { delete this.map[url]; } ,deleteMissingThreads : function(list) { //listに無いエントリを削除する for(var i in this.map) { if (!list[i]) { delete this.map[i]; } } } }; function parseResponseDocument(conf, doc, onParseFinished) { updateManager.init(conf); var topLevelNodes = doc.documentElement.childNodes; var entities = {}; //id -> threadInfo for(var i = 0; i < topLevelNodes.length; i++) { var groupNode = topLevelNodes.item(i); if (!(groupNode instanceof org.w3c.dom.Element)) { continue; } var tagName = ''+groupNode.nodeName; if (tagName == "entities") { for (var j = 0; j < groupNode.childNodes.length ; j++) { var node = groupNode.childNodes.item(j); if (!(node instanceof org.w3c.dom.Element) ) { continue; } var tagName = node.nodeName+""; var threadInfo = ThreadInfo.fromElement(node) ; entities[threadInfo.id] = threadInfo; if (threadInfo.isBoard == false) { var th = null; var url = threadInfo.url + ''; if (threadRepository.contains(url)) { th = threadRepository.get(url); } else { th = v2c.getThread(url, threadInfo.title); if (th) { debug(url+","+ threadInfo.title); threadRepository.putIfMissing( th); } } if (th) { updateManager.addThread(th, threadInfo); } } } } else if (tagName == "thread_group") { var status = attr(groupNode, 's'); if (status == "n") continue; var category = attr(groupNode, 'category'); var tabCategory = "open"; var favCategory = "favorite"; if (category == tabCategory) { //タブ一覧 parseTabGroupElement(conf, groupNode, entities); } else if (category == favCategory) { //お気に入り一覧 var targetFavoriteTab = getFavTabForSync(conf); if (targetFavoriteTab) { allThreads = getAllThreads(targetFavoriteTab, targetFavoriteTab.root); insertFavRecurse(targetFavoriteTab, groupNode, targetFavoriteTab.root, allThreads, entities); } } } } if (onParseFinished) { onParseFinished(); //時間がかかる更新の前に同期番号を保存しておく。 } updateManager.updateThreads(conf); } function parseTabGroupElement(conf, groupNode, entities) { var column = getSyncTabColumn(conf); if (!column) return; var selectedTh = column.selectedThread; var skippedThreads = {}; var allThreads = {}; for (var tabIndex = column.tabCount - 1; tabIndex >= 0; tabIndex--) { var th = column.threads[tabIndex]; if (th) { allThreads[th.url+""] = th; } } idList = []; var urlMap = {}; var collectThList = function(element) { for (var j = 0; j < element.childNodes.length ; j++) { var node = element.childNodes.item(j); if ((node instanceof org.w3c.dom.Element) && node.nodeName+"" == "dir") { collectThList(node); continue; } if (!(node instanceof org.w3c.dom.Element) || node.nodeName+"" != "th") { continue; } var idAttr = attr(node, 'id'); if (idAttr == undefined || !entities[idAttr]) continue; var threadInfo = entities[idAttr] ; urlMap[threadInfo.url+""] = threadInfo; idList.push(threadInfo); } }; collectThList(groupNode); //削除 for (var url in allThreads) { if (!urlMap[url]) { var th = allThreads[url]; if (canSyncThread(th)) { if (selectedTh) { if (th == selectedTh || th.url+"" == selectedTh.url+"") { selectedTh = null; } } th.close(); } } } var insertIndex = 0; if (idList != null) for (var j = 0; j < idList.length ; j++) { var threadInfo = idList[j] ; //挿入可能位置まで進める insertIndex = proceedInsertIndex(column, insertIndex); var sameThread = false; var th = allThreads[threadInfo.url]; if (threadRepository.contains(threadInfo.url)) { th = threadRepository.get(threadInfo.url); } else if (th == null) { th = v2c.getThread(threadInfo.url, threadInfo.title); } if (th== null) { continue; } if (insertIndex < column.tabCount) { var existTh = column.threads[insertIndex]; if (existTh.url+'' == threadInfo.url) { //一致 sameThread = true; } } if (!sameThread) { if (th.columnIndex == -1) { column.openThread(th, false, true, true); } th.movePanelTo(conf.tabColumn, insertIndex >= column.tabCount ? -1 : insertIndex); } insertIndex++; } for(var k = column.tabCount - 1 ; k >= insertIndex; k--) { var th = column.threads[k]; if (canSyncThread(th)) column.threads[k].close(); } if (selectedTh) { column.openThread(selectedTh, false, false, false); } } function proceedInsertIndex(column, insertIndex) { for(var tabIndex = insertIndex; tabIndex < column.tabCount; tabIndex++) { var th = column.threads[tabIndex]; if (canSyncThread(th)) { return tabIndex; } } return column.tabCount; } /** * お気に入りには複数のスレッドを登録できないので、 * 削除時のため、全てのスレッドを参照しておく。 */ function getAllThreads(favTab, folder) { var threads = {}; for (var c = 0; c < folder.childCount; c++) { var child = folder.getChild(c); if (child.thread) { threads[child.thread.url+""] = child; }else if (child.board) { threads[child.board.url+""] = child; } else if (child.childCount > 0) { var childThreads = getAllThreads(favTab, child); for(var url in childThreads) { threads[url] = childThreads[url]; } } } return threads; } function fixBoardUrl(boardName) { return boardName.replace("shitaraba.com", "livedoor.jp"); } /** * お気に入りを受け取ったXMLの順番で格納していく再帰関数 * フォルダはinsertItem関数を使うと閉じてしまうため対策が必要。 */ function insertFavRecurse(favTab, groupNode, folder, removedAllThreads, entities) { var folders = {}; var threads = {}; var deleteFolders = {}; var deleteThreads = {}; var deleteBoards = {}; //ハッシュマップの生成 url->thread, label->folder //と消去アイテムの削除 if (folder.childCount > 0) { for (var c = folder.childCount-1; c >= 0; c--) { var child = folder.getChild(c); if (child) { if (child.thread){ threads[child.thread.url+""] = child.thread; deleteThreads[child.thread.url+""] = child.thread; } else if (child.board) { threads[child.board.url+""] = child.board; deleteBoards[fixBoardUrl(child.board.url+"")] = child.board; } else if (child.label){ //フォルダ folders[child.label+""] = child; deleteFolders[child.label+""] = child; } } } } var targetList = [];//groupNode.childNodes; for (var j=0; j < groupNode.childNodes.length ; j++) { var node = groupNode.childNodes.item(j); if (!(node instanceof org.w3c.dom.Element)) { continue; } if (node.nodeName+"" == "dir") { var name = Crypt.decFolder(attr(node, "name")); targetList.push({tagName:'dir', "name": name, element: node}); delete deleteFolders[name]; } else if (node.nodeName+"" == "th") { var idAttr = attr(node, "id"); var url = entities[idAttr].url; delete deleteThreads[url]; targetList.push({tagName:'th', "id": idAttr}); } else if (node.nodeName+"" == "bd") { var idAttr = attr(node, "id"); var url = entities[idAttr].url;//attr(node, "url"); targetList.push({tagName:'bd', "id": idAttr}); delete deleteBoards[url]; } } for(var i in deleteBoards) { favTab.removeItem(deleteBoards[i]); delete threads[i]; } for(var i in deleteThreads) { var th = deleteThreads[i]; if (canSyncThread(th)) { favTab.removeItem(th); delete threads[i]; } } for(var i in deleteFolders) { favTab.removeItem(deleteFolders[i]); delete folders[i]; } //挿入と並び替え var insertIndex = 0; for (var j=0; j < targetList.length ; j++) { var node = targetList[j]; var currentChild = null; if (insertIndex < folder.childCount) { currentChild = folder.getChild(insertIndex); } if (node.tagName+"" == "dir") { var name = node.name; var childFolder = null; if (currentChild && currentChild.label+"" == name && !currentChild.thread && !currentChild.board) { childFolder = currentChild; //現並びと一致 } else if (folders[name]) { favTab.insertItem(folder, folders[name], insertIndex); childFolder = folders[name]; } else { //新規フォルダ childFolder = favTab.insertFolder(folder, name, insertIndex); } insertIndex++; if (childFolder && attr(node.element, 's') != "n") { insertFavRecurse(favTab, node.element, childFolder, removedAllThreads, entities); } } else if (node.tagName+"" == "th") { var idAttr = node.id; if (idAttr == undefined || !entities[idAttr]) { continue; } var threadInfo = entities[idAttr]; var url = threadInfo.url + ""; if (currentChild && currentChild.thread && currentChild.thread.url+"" == url) { delete threads[url]; } else { if (removedAllThreads[url]) { favTab.removeItem(removedAllThreads[url]); delete removedAllThreads[url]; } var th = null; if (threads[url]) { th = threads[url]; favTab.removeItem(threads[url]); } else if (threadRepository.contains(url)) { th = threadRepository.get(url); } else if (th == null) { th = v2c.getThread(url, threadInfo.title); } if (th) { favTab.insertItem(folder, th, insertIndex); } } insertIndex++; } else if (node.name+"" == "bd") { var idAttr = node.id; if (idAttr == undefined || !entities[idAttr]) { continue; } var boardInfo = entities[idAttr]; var url = boardInfo.url; if (currentChild && currentChild.board && currentChild.board.url+"" == url) { delete threads[url]; } else { if (removedAllThreads[url]) { favTab.removeItem(removedAllThreads[url]); delete removedAllThreads[url]; } var bd = null; if (threads[url]) { bd = threads[url]; favTab.removeItem(threads[url]); } else { bd = v2c.getBoard(url); } if (bd) { favTab.insertItem(folder, bd, insertIndex); } } insertIndex++; } } } //設定画面を起動 function startConfigure() { v2c.context.setPopupHTML(generateConfHTML()); v2c.context.setTrapFormSubmission(true); v2c.context.setPopupFocusable(true); } function generateConfHTML() { var conf = getConfObject(); var noConf = conf == null; //初期起動時にはこの変数を用いてデフォルト値を設定する。 var id = noConf ? "" : conf.id; var pass = noConf ? "" : conf.password; var tab = noConf ? true : (conf.tab ? true : false); var fav = noConf ? true : (conf.fav ? true : false); var post = noConf ? false : (conf.post ? true : false); var remainTab = noConf ? false : (conf.remainTab==undefined?false:(conf.remainTab ? true : false)); var favTab = noConf ? -1 : conf.favTab; var tabColumn = noConf ? -1 : conf.tabColumn; var cryptLevel = (noConf || conf.cryptLevel == undefined)? 0 : conf.cryptLevel; var cryptPass = (noConf || conf.cryptPass == undefined)? "" : conf.cryptPass; var span = (noConf || isNaN(conf.span)) ? DEFAULT_SYNC_SPAN_MINUTES : conf.span; var html = ''; html += ''; html += "

Sync2ch同期設定

"; html += '
'+CLIENT_NAME+ ' ' +CLIENT_VERSION+' (http://sync2ch.com)
'; html += '
'; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; //html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; //html += ''; html += '
ID :
API接続用パスワード :
タブ一覧を同期する :
お気に入りを同期する :
書き込んだレス番号を同期する :
(プレミアムアカウントのみ)  

新規スレをタブに表示 :
(現在位置の正確な同期のため)  
同期用タブカラム :
同期用お気に入りタブ :

同期データ暗号化 :
暗号化用パスワード :
自動同期間隔(分) :

'; html += '
'; html += '  '; html += '
'; html += '
'; html += ''; html += ''; return html; } function formSubmitted(u,sm,sd) { var prefs = sd.split('&'); var confText = ""; var isCanceled = false; var id = null; var pass = null; for(var i in prefs) { var nameAndValue = prefs[i].split('='); var name = nameAndValue[0]; var value = nameAndValue[1]; if (name == 'cancel') { isCanceled = true; break; } if (name == 'id') { id = value; value = e(value); } else if (name == 'password'){ pass = value; value = e(value); } else if (name == 'cryptPass') { value = e(value); } else { value = "\""+value+"\""; } confText += name+':'+value+",\n\t"; debug(confText); } if (!isCanceled) { var idFile = v2c.getScriptDataFile(CONF_FILENAME); v2c.writeStringToFile(idFile, "({\n\t"+confText+"\n})"); } v2c.context.closeOriginalPopup(); //同期再開 if (!isCanceled && !hasArg(ARG_OPTION) && id && pass) { trySync(false); } } //設定オブジェクトを取得 function getConfObject() { var idFile = v2c.getScriptDataFile(CONF_FILENAME); if (idFile.exists()) { var confText = v2c.readFile(idFile); v2c.println(confText); var conf = eval(confText+""); if (conf) { conf.password = d(conf.password); conf.id = d(conf.id); conf.cryptPass = d(conf.cryptPass); return conf; } } return null; } function getFavTabForSync(conf) { var targetFavTab = v2c.favorites.getFavorite(conf.favTab); if (!targetFavTab) { for (var i = 0; i < v2c.favorites.count ; i++) { targetFavTab = v2c.favorites.getFavorite(i); break; } } return targetFavTab; } function getSyncTabColumn(conf) { var column = null; var columnCount = v2c.resPane.columnCount; if (conf.tabColumn < columnCount) { column = v2c.resPane.columns[conf.tabColumn]; } else if (columnCount > 0) { conf.tabColumn = 0; column = v2c.resPane.columns[0]; } return column; } function e(value) { var sksSpec = new javax.crypto.spec.SecretKeySpec(key().getBytes(), "Blowfish"); var cipher = javax.crypto.Cipher.getInstance("Blowfish"); cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, sksSpec); var fin = cipher.doFinal(new java.lang.String(value).getBytes()); var arr = []; for (var i = 0; i < fin.length; i++) { arr[i] = fin[i]; } var str = "[" + arr.join(',') + "]"; //v2c.println(str); return str; } function key() { var p = "I3"; var ver = java.lang.System.getProperty("os.version")+""; for(var i = 0; i < ver.length && i < 3; i++) p+=ver[i]; var ver = java.lang.System.getProperty("user.name")+""; for(var i = 0; i < ver.length && i < 4; i++) p+=ver[i]; return new java.lang.String(p+"c"); } function d(value) { try { //v2c.println("value = " + value); //v2c.println(v2c.scriptEngineName); if (v2c.scriptEngineName.indexOf('Nashorn') != -1) { var javaArray = Java.to(value,"byte[]"); } else { var javaArray = new Packages.java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, value.length); for(var i = 0; i < value.length; i++) { javaArray[i] = value[i]; } } //print(JavaArray[0]+JavaArray1+JavaArray[2]); var sksSpec = new javax.crypto.spec.SecretKeySpec(key().getBytes(), "Blowfish"); var cipher = javax.crypto.Cipher.getInstance("Blowfish"); cipher.init(javax.crypto.Cipher.DECRYPT_MODE, sksSpec); var temp = cipher.doFinal(javaArray); //v2c.println(temp); return ""+new java.lang.String(temp); } catch(e) { v2c.println(e.message); return ""; } } /* CryptoJS v3.1.2 code.google.com/p/crypto-js (c) 2009-2013 by Jeff Mott. All rights reserved. code.google.com/p/crypto-js/wiki/License */ var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}}, r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<< 32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j>>3]|=parseInt(a.substr(j, 2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}}, q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w< l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})(); (function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])}, _doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]), f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f, m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m, E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/ 4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math); (function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a, this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684, 1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})}, decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d, b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}(); (function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8, 16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;dd||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>> 8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t= d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})(); var Base64 = { encode : function (input) { return CryptoJS.enc.Base64.stringify( CryptoJS.enc.Utf8.parse(input) ); }, } var Crypt = { cryptLevel: 0 , postNumsXorPattern: 154 , nowXorPattern:212 , readXorPattern: 113 , countXorPattern:45 , getZeroBytes: function() { if (this.zeroBytes == null) { this.zeroBytes = this.genIvBytes(""); } return this.zeroBytes; } , setKey: function(level, key) { this.cryptLevel = Number(level); if (isNaN(this.cryptLevel) || (this.cryptLevel < 0 && 6 <= this.cryptLevel)) { this.cryptLevel = 0; } this.keyBytes = null; if (key) { this.keyBytes = CryptoJS.enc.Utf8.parse(""+key); this.keyBytes.sigBytes = 16; } if (this.keyBytes == null) { this.cryptLevel = 0; } } , removeKey: function (){ this.cryptLevel = 0; this.keyBytes = null; } , encPostNums: function(nums, url) { if (this.cryptLevel == 0) { return nums.join(','); } var ivBytes = this.genIvBytes(this.reverseString(url)); this.applyXorBytePattern(ivBytes.words, this.postNumsXorPattern); var list = []; for (var i in nums) { list.push( this.enc(nums[i]+"", ivBytes)); } return list.join(','); } , decPostNums: function(text, url) { var list = text ? text.split(',') : []; if (this.cryptLevel == 0) { return list; } var outList = []; var ivBytes = this.genIvBytes(this.reverseString(url)); this.applyXorBytePattern(ivBytes.words, this.postNumsXorPattern); for (var i in list) { var decoded = this.dec(list[i], ivBytes); outList.push(decoded); } return outList; } ,reverseString: function(str) { str = str+""; var result = ""; for (var i= str.length-1; i >= 0; i--) { result += str[i]; } return result; } , encUrl: function(url) { return this.cryptLevel < 3 ? this.escapeXML(url) : this.enc(this.reverseString(url+""), this.getZeroBytes()); } , decUrl: function(text) { return this.cryptLevel < 3 ? text : this.reverseString( ""+this.dec(text+"", this.getZeroBytes())); } , encTitle: function(title, url) { return this.cryptLevel < 2 ? this.escapeXML(title) : this.enc(title, this.genIvBytes(this.reverseString(""+url))); } , decTitle: function(text, url) { return this.cryptLevel < 2 ? text : this.dec(text, this.genIvBytes(this.reverseString(url))); } , encFolder: function(name, url) { return this.cryptLevel < 4 ? this.escapeXML(name) : this.enc(name, this.getZeroBytes());} , decFolder: function(text, url) { return this.cryptLevel < 4 ? text : this.dec(text, this.getZeroBytes());} , encNow: function(pos, url) { return this.cryptLevel < 5 ? pos : this.encNum(pos, url, this.nowXorPattern);} , decNow: function(text, url) { return this.cryptLevel < 5 ? text : this.decNum(text, url, this.nowXorPattern); } , encRead: function(read, url) { return this.cryptLevel < 5 ? read : this.encNum(read, url, this.readXorPattern); } , decRead: function(text, url) { return this.cryptLevel < 5 ? text : this.decNum(text, url, this.readXorPattern); } , encCount: function(count, url) { return this.cryptLevel < 5 ? count : this.encNum(count, url, this.countXorPattern); } , decCount: function(text, url) { return this.cryptLevel < 5 ? text : this.decNum(text, url, this.countXorPattern); } , encNum : function(pos, url, pattern) { var ivBytes = this.genIvBytes(this.reverseString(url)); this.applyXorBytePattern(ivBytes.words, pattern); return this.enc(new String(pos), ivBytes); } , decNum: function(pos, url, pattern) { var ivBytes = this.genIvBytes(this.reverseString(url)); this.applyXorBytePattern(ivBytes.words, pattern); return this.dec(pos, ivBytes); } , genIvBytes: function(str) { try { var ivBytes = CryptoJS.enc.Utf8.parse(str); ivBytes.sigBytes = 16; return ivBytes; }catch(e) { v2c.println(e); v2c.println(str); return str; } } , applyXorBytePattern : function(words, xorPatternByte) { if (!words) { return ; } for (i = 0; i < 16; ++i) { word = words[i]; words[i] = ((((word >> 24) & 0xFF) ^ xorPatternByte) << 24) |((((word >> 16) & 0xFF) ^ xorPatternByte) << 16) |((((word >> 8) & 0xFF) ^ xorPatternByte) << 8) |(((word & 0xFF) ^ xorPatternByte) << 0); } } , escapeXML: function(str) { str = str + ''; return str.replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } , enc: function(str, ivBytes) { if (this.keyBytes == null) return str; try { var encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(str), this.keyBytes, { iv: ivBytes}); } catch(e) { v2c.println(e); return str; } return encrypted; } , dec: function(str, ivBytes) { if (str == null) return ""; if (this.keyBytes == null) return str; try { var decrypted = CryptoJS.AES.decrypt(str, this.keyBytes, { iv:ivBytes }); }catch(e) { v2c.println(e); return str; } var byteArray = this.wordToByteArray(decrypted.words, decrypted.sigBytes); return this.utf8ByteArrayToString(byteArray); } , utf8ByteArrayToString : function(bytes) { var out = [], pos = 0, c = 0; while (pos < bytes.length) { var c1 = bytes[pos++]; if (c1 < 128) { out[c++] = String.fromCharCode(c1); } else if (c1 > 191 && c1 < 224) { var c2 = bytes[pos++]; out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63); } else { var c2 = bytes[pos++]; var c3 = bytes[pos++]; out[c++] = String.fromCharCode( (c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63); } } return out.join(''); } , wordToByteArray: function(wordArray, sigBytes) { var byteArray = [], word, i, j; var byteCount = 0; for (i = 0; i < wordArray.length; ++i) { word = wordArray[i]; for (j = 3; j >= 0; --j) { byteArray.push((word >> 8 * j) & 0xFF); byteCount ++; if (byteCount >= sigBytes) break; } if (byteCount >= sigBytes) break; } return byteArray; } }; try { main(); } catch(e) { var message = "■エラー " + e.lineNumber+ "行目: "+ e.toString(); v2c.println(message); v2c.context.setStatusBarText(message); }