6 İşlemeler f6cc11ef5d ... 22a06fb869

Yazar SHA1 Mesaj Tarih
  NanashiNoGombe 22a06fb869 KeyManager: cookie manager html adjustment 1 hafta önce
  NanashiNoGombe 372db38bf0 Auth: uplift account management (experimental, not tested) 1 hafta önce
  NanashiNoGombe 7d5cf88016 Auth: refactor 1 hafta önce
  NanashiNoGombe 91cc21e9a9 Auth: be account management 1 hafta önce
  NanashiNoGombe d254c4690e Auth: refactor 1 hafta önce
  NanashiNoGombe 35c1f2361f Auth: refactor 1 hafta önce

+ 308 - 47
BBS2chProxyAuth.cpp

@@ -7,7 +7,6 @@
 #include "BBS2chProxyAuth.h"
 #include "BBS2chProxyHttpHeaders.h"
 #include "BBS2chProxyConnection.h"
-#include "BBS2chProxyFormData.h"
 #include "hmac.h"
 
 #define SID_UPDATE_INTERVAL (60*60)
@@ -30,11 +29,11 @@ static size_t write_callback_download(char *buffer, size_t size, size_t nitems,
 {
 	std::vector<char> *data = static_cast<std::vector<char> *>(userdata);
 	size_t downloaded = size*nitems;
-	data->insert(data->end(), buffer, buffer+downloaded);
+	if (data) data->insert(data->end(), buffer, buffer+downloaded);
 	return downloaded;
 }
 
-static size_t header_callback_download(char *buffer, size_t size, size_t nitems, void *userdata)
+static size_t header_callback_acorn(char *buffer, size_t size, size_t nitems, void *userdata)
 {
 	std::string *acornCookie = static_cast<std::string *>(userdata);
 	if (acornCookie && acornCookie->empty()) {
@@ -52,6 +51,53 @@ static size_t header_callback_download(char *buffer, size_t size, size_t nitems,
 	return size*nitems;
 }
 
+static size_t header_callback_be(char *buffer, size_t size, size_t nitems, void *userdata)
+{
+	std::pair<std::string, std::string> *beCookies = static_cast<std::pair<std::string, std::string> *>(userdata);
+	if (beCookies) {
+		PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
+		if (parsedHeader && parsedHeader->getLowercasedName() == "set-cookie") {
+			if (beCookies->first.empty()) {
+				const std::string dmdmStr = "DMDM=";
+				const std::string &value = parsedHeader->getValue();
+				if (std::equal(dmdmStr.begin(), dmdmStr.end(), value.begin())) {
+					size_t pos = value.find(";");
+					if (pos == std::string::npos) beCookies->first = value.substr(5);
+					else beCookies->first = value.substr(5, pos-5);
+				}
+			}
+			if (beCookies->second.empty()) {
+				const std::string mdmdStr = "MDMD=";
+				const std::string &value = parsedHeader->getValue();
+				if (std::equal(mdmdStr.begin(), mdmdStr.end(), value.begin())) {
+					size_t pos = value.find(";");
+					if (pos == std::string::npos) beCookies->second = value.substr(5);
+					else beCookies->second = value.substr(5, pos-5);
+				}
+			}
+		}
+	}
+	return size*nitems;
+}
+
+static size_t header_callback_uplift(char *buffer, size_t size, size_t nitems, void *userdata)
+{
+	std::string *sidCookie = static_cast<std::string *>(userdata);
+	if (sidCookie && sidCookie->empty()) {
+		PBBS2chProxyHttpHeaderEntry parsedHeader = BBS2chProxyHttpHeaders::parse(buffer, size*nitems);
+		if (parsedHeader && parsedHeader->getLowercasedName() == "set-cookie") {
+			const std::string sidStr = "sid=";
+			const std::string &value = parsedHeader->getValue();
+			if (std::equal(sidStr.begin(), sidStr.end(), value.begin())) {
+				size_t pos = value.find(";");
+				if (pos == std::string::npos) *sidCookie = value.substr(4);
+				else *sidCookie = value.substr(4, pos-4);
+			}
+		}
+	}
+	return size*nitems;
+}
+
 bool BBS2chProxyAuth::updateSID(CURL *curl)
 {
 	time_t ct = time(0);
@@ -171,30 +217,41 @@ const std::string& BBS2chProxyAuth::getSID()
 	return sid;
 }
 
-bool BBS2chProxyAcornAuth::login()
+void IBBS2chProxyCookieAuth::prepareForLogin(CURL *curl, const std::string &authURL, BBS2chProxyFormData &requestBody)
 {
-	bool ret = false;
-	CURL *curl = curl_easy_init();
-	std::vector<char> data;
-	std::string cookie;
-	std::string body = "email=";
-	body += BBS2chProxyFormData::encodeURIComponent(_mail.data(), _mail.size(), false);
-	body += "&pass=";
-	body += BBS2chProxyFormData::encodeURIComponent(_passwd.data(), _passwd.size(), false);
-	curl_slist *headersForCurl = curl_slist_append(NULL, "Accept:");
-	headersForCurl = curl_slist_append(headersForCurl, "Expect:");
-	curl_easy_setopt(curl, CURLOPT_URL, "https://donguri.5ch.net/login");
-	if (user_agent) curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
+	
+	curl_easy_setopt(curl, CURLOPT_URL, authURL.c_str());
 	curl_easy_setopt(curl, CURLOPT_ENCODING, "");
 	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
 	curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
 	curl_easy_setopt(curl, CURLOPT_POST, 1L);
-	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
+	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, requestBody.toString().c_str());
 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_download);
-	curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
-	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_download);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
+	curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
+	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+	if (curl_share) curl_easy_setopt(curl, CURLOPT_SHARE, curl_share);
+	if (user_agent) curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
+	if (force_ipv4) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+	if (proxy_server) {
+		curl_easy_setopt(curl, CURLOPT_PROXY, proxy_server);
+		curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy_port);
+		curl_easy_setopt(curl, CURLOPT_PROXYTYPE, proxy_type);
+	}
+}
+
+bool BBS2chProxyAcornAuth::login()
+{
+	bool ret = false;
+	CURL *curl = curl_easy_init();
+	std::string cookie;
+	BBS2chProxyFormData requestBody;
+	requestBody.append("email", _mail);
+	requestBody.append("pass", _passwd);
+	prepareForLogin(curl, "https://donguri.5ch.net/login", requestBody);
+	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_acorn);
 	curl_easy_setopt(curl, CURLOPT_HEADERDATA, &cookie);
-	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headersForCurl);
 	CURLcode res = curl_easy_perform(curl);
 	if (res == CURLE_OK) {
 		long statusCode;
@@ -222,7 +279,7 @@ bool BBS2chProxyAcornAuth::login()
 	return ret;
 }
 
-int BBS2chProxyAcornAuth::login(const std::string &mail, const std::string &passwd)
+int IBBS2chProxyCookieAuth::loginWith(const std::string &mail, const std::string &passwd)
 {
 	pthread_mutex_lock(&_mutex);
 	if (!_isUpdating) {
@@ -248,15 +305,15 @@ void BBS2chProxyAcornAuth::logout()
 
 static void *doLogin(void *auth)
 {
-	((BBS2chProxyAcornAuth *)auth)->login();
+	((IBBS2chProxyCookieAuth *)auth)->login();
 	return NULL;
 }
 
-void BBS2chProxyAcornAuth::update(bool force)
+void IBBS2chProxyCookieAuth::update(bool force)
 {
-	if (_cookie.empty()) return;
+	if (!isLoggedIn()) return;
 	pthread_mutex_lock(&_mutex);
-	if (!_isUpdating && (force || time(NULL) >= _lastUpdated + 60*60*12)) {
+	if (!_isUpdating && (force || time(NULL) >= _lastUpdated + _updateInterval)) {
 		_isUpdating = true;
 		pthread_t thread;
 		pthread_create(&thread, NULL, &doLogin, this);
@@ -265,11 +322,11 @@ void BBS2chProxyAcornAuth::update(bool force)
 	pthread_mutex_unlock(&_mutex);
 }
 
-void BBS2chProxyAcornAuth::syncCookie(BBS2chProxyKeyManager::CookieJar &jar)
+bool BBS2chProxyAcornAuth::syncCookie(BBS2chProxyKeyManager::CookieJar &jar)
 {
 	if (jar.timeAcornSynced >= _lastUpdated) {
 		update(false);
-		return;
+		return false;
 	}
 	BBS2chProxyKeyManager::Cookie cookie;
 	cookie.name = "acorn";
@@ -280,40 +337,244 @@ void BBS2chProxyAcornAuth::syncCookie(BBS2chProxyKeyManager::CookieJar &jar)
 	jar.set(cookie);
 	jar.timeAcornSynced = _lastUpdated;
 	jar.unlock();
-	BBS2chProxyConnection::keyManager.flushCookies();
+	return true;
 }
 
-std::string BBS2chProxyAcornAuth::responseHTML()
+std::string IBBS2chProxyCookieAuth::responseHTML()
 {
 	std::string html;
-	html += "<!DOCTYPE html><html lang=\"ja\"><head>";
-	html += "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">";
-	html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><style>";
-	html += ".container { width: 300px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }";
-	html += "h3 { text-align: center; margin-bottom: 20px; margin-top: 0px; }";
-	html += "input[type=\"text\"], input[type=\"password\"] { width: 93%; padding: 10px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px; }";
-	html += "button[type=\"submit\"] { width: 100%; padding: 10px; background-color: #4CAF50; color: #ffffff; border: none; border-radius: 4px; cursor: pointer; }";
-	html += "</style></head><body>";
-	if (_cookie.empty()) {
-		html += "<div class=\"container\"><h3>どんぐりシステムにログイン</h3>";
-		html += "<form action=\"donguri\" method=\"POST\">";
+	html += "<div class=\"container\"><h3>";
+	html += _headerTitle;
+	html += "にログイン</h3>";
+	if (!isLoggedIn()) {
+		html += "<form id=\"";
+		html += _uniqueId;
+		html += "-login\"action=\"accounts\" method=\"POST\">";
 		html += "<input type=\"text\" name=\"mail\" placeholder=\"メールアドレス\">";
 		html += "<input type=\"password\" name=\"pass\" placeholder=\"パスワード\">";
 		html += "<input type=\"hidden\" name=\"action\" value=\"login\">";
-		html += "<button type=\"submit\">ログイン</button></form></div>";
+		html += "<input type=\"hidden\" name=\"service\" value=\"";
+		html += _uniqueId;
+		html += "\"><button type=\"submit\">ログイン</button></form></div>";
 	} else {
 		std::ostringstream ss;
 		time_t ago = time(NULL) - _lastUpdated;
 		html += "<p>";
-		if (ago < 3600) ss << ago/60 << " 分前に";
-		else ss << ago/3600 << " 時間前に";
+		if (ago < 3600) ss << ago/60 << " 分前にログイン済みです。</p>";
+		else ss << ago/3600 << " 時間前にログイン済みです。</p>";
 		html += ss.str();
-		html += "どんぐりシステムにログイン済みです。</p>";
-		html += "<div class=\"container\">";
-		html += "<form action=\"donguri\" method=\"POST\">";
+		html += "<form action=\"accounts\" method=\"POST\">";
 		html += "<input type=\"hidden\" name=\"action\" value=\"logout\">";
-		html += "<button type=\"submit\">ログアウト</button></form></div>";
+		html += "<input type=\"hidden\" name=\"service\" value=\"";
+		html += _uniqueId;
+		html += "\"><button type=\"submit\">ログアウト</button></form>";
+	}
+	html += "</div>";
+	return html;
+}
+
+bool BBS2chProxyAcornAuth::isLoggedIn()
+{
+	return !_cookie.empty();
+}
+
+bool BBS2chProxyBeAuth::login()
+{
+	bool ret = false;
+	CURL *curl = curl_easy_init();
+	std::pair<std::string, std::string> cookies;
+	BBS2chProxyFormData requestBody;
+	requestBody.append("mail", _mail);
+	requestBody.append("pass", _passwd);
+	prepareForLogin(curl, "https://be.5ch.net/log", requestBody);
+	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_be);
+	curl_easy_setopt(curl, CURLOPT_HEADERDATA, &cookies);
+	CURLcode res = curl_easy_perform(curl);
+	if (res == CURLE_OK) {
+		long statusCode;
+		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
+		if (statusCode == 302) {
+			if (!cookies.first.empty() && !cookies.second.empty()) {
+				log_printf(0, "Logged in to be successfully.\n");
+				log_printf(1, "New cookie is: DMDM=%s, MDMD=%s\n", cookies.first.c_str(), cookies.second.c_str());
+				pthread_mutex_lock(&_mutex);
+				_dmdm = cookies.first;
+				_mdmd = cookies.second;
+				_lastUpdated = time(NULL);
+				pthread_mutex_unlock(&_mutex);
+				ret = true;
+			}
+			else log_printf(0, "Cannot login to be (maybe incorrect user/password).\n");
+		} else {
+			log_printf(0, "Cannot login to be: server returned %ld\n", statusCode);
+		}
+	}
+	else {
+		log_printf(0, "curl error: %s\n", curl_easy_strerror(res));
+	}
+	curl_easy_cleanup(curl);
+	pthread_mutex_lock(&_mutex);
+	_isUpdating = false;
+	pthread_mutex_unlock(&_mutex);
+	return ret;
+}
+
+void BBS2chProxyBeAuth::logout()
+{
+	pthread_mutex_lock(&_mutex);
+	_mail.clear();
+	_passwd.clear();
+	_dmdm.clear();
+	_mdmd.clear();
+	_lastUpdated = time(NULL);
+	pthread_mutex_unlock(&_mutex);
+}
+
+bool BBS2chProxyBeAuth::syncCookie(BBS2chProxyKeyManager::CookieJar &jar)
+{
+	if (jar.timeBeSynced >= _lastUpdated) {
+		update(false);
+		return false;
+	}
+	BBS2chProxyKeyManager::Cookie dmdm, mdmd;
+	dmdm.name = "DMDM";
+	dmdm.value = _dmdm;
+	dmdm.domain = ".5ch.net";
+	if (_dmdm.empty()) dmdm.expires = "1";
+	mdmd.name = "MDMD";
+	mdmd.value = _mdmd;
+	mdmd.domain = ".5ch.net";
+	if (_mdmd.empty()) mdmd.expires = "1";
+	jar.lock();
+	jar.set(dmdm);
+	jar.set(mdmd);
+	jar.timeBeSynced = _lastUpdated;
+	jar.unlock();
+	return true;
+}
+
+bool BBS2chProxyBeAuth::isLoggedIn()
+{
+	return !_dmdm.empty() && !_mdmd.empty();
+}
+
+bool BBS2chProxyUpliftAuth::login()
+{
+	bool ret = false;
+	CURL *curl = curl_easy_init();
+	std::string cookie;
+	BBS2chProxyFormData requestBody;
+	requestBody.append("usr", _mail);
+	requestBody.append("pwd", _passwd);
+	prepareForLogin(curl, "http://uplift.5ch.net/log", requestBody);
+	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_uplift);
+	curl_easy_setopt(curl, CURLOPT_HEADERDATA, &cookie);
+	CURLcode res = curl_easy_perform(curl);
+	if (res == CURLE_OK) {
+		long statusCode;
+		curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode);
+		if (!cookie.empty()) {
+			log_printf(0, "Logged in to uplift successfully.\n");
+			log_printf(1, "New cookie is: %s\n", cookie.c_str());
+			pthread_mutex_lock(&_mutex);
+			_cookie = cookie;
+			_lastUpdated = time(NULL);
+			pthread_mutex_unlock(&_mutex);
+			ret = true;
+		} else {
+			if (statusCode == 200) log_printf(0, "Cannot login to uplift (maybe incorrect user/password).\n");
+			else log_printf(0, "Cannot login to uplift: server returned %ld\n", statusCode);
+		}
 	}
+	else {
+		log_printf(0, "curl error: %s\n", curl_easy_strerror(res));
+	}
+	curl_easy_cleanup(curl);
+	pthread_mutex_lock(&_mutex);
+	_isUpdating = false;
+	pthread_mutex_unlock(&_mutex);
+	return ret;
+}
+
+void BBS2chProxyUpliftAuth::logout()
+{
+	pthread_mutex_lock(&_mutex);
+	_mail.clear();
+	_passwd.clear();
+	_cookie.clear();
+	_lastUpdated = time(NULL);
+	pthread_mutex_unlock(&_mutex);
+}
+
+bool BBS2chProxyUpliftAuth::syncCookie(BBS2chProxyKeyManager::CookieJar &jar)
+{
+	if (jar.timeBeSynced >= _lastUpdated) {
+		update(false);
+		return false;
+	}
+	BBS2chProxyKeyManager::Cookie for5ch, forPink;
+	for5ch.name = "sid";
+	for5ch.value = _cookie;
+	for5ch.domain = ".5ch.net";
+	forPink.name = "sid";
+	forPink.value = _cookie;
+	forPink.domain = ".bbspink.com";
+	if (_cookie.empty()) {
+		for5ch.expires = "1";
+		forPink.expires = "1";
+	}
+	jar.lock();
+	jar.set(for5ch);
+	jar.set(forPink);
+	jar.timeUpliftSynced = _lastUpdated;
+	jar.unlock();
+	return true;
+}
+
+bool BBS2chProxyUpliftAuth::isLoggedIn()
+{
+	return !_cookie.empty();
+}
+
+int BBS2chProxyCookieAuthManager::loginWith(const std::string &service, const std::string &mail, const std::string &passwd)
+{
+	if (service == "donguri") return _acornAuth.loginWith(mail, passwd);
+	else if (service == "be") return _beAuth.loginWith(mail, passwd);
+	else if (service == "uplift") return _upliftAuth.loginWith(mail, passwd);
+	return 2;
+}
+
+void BBS2chProxyCookieAuthManager::logout(const std::string &service)
+{
+	if (service == "donguri") _acornAuth.logout();
+	else if (service == "be") _beAuth.logout();
+	else if (service == "uplift") _upliftAuth.logout();
+}
+
+void BBS2chProxyCookieAuthManager::syncCookies(BBS2chProxyKeyManager::CookieJar &jar)
+{
+	bool shouldFlush = false;
+	if (_acornAuth.syncCookie(jar)) shouldFlush = true;
+	if (_beAuth.syncCookie(jar)) shouldFlush = true;
+	if (_upliftAuth.syncCookie(jar)) shouldFlush = true;
+	if (shouldFlush) BBS2chProxyConnection::keyManager.flushCookies();
+}
+
+std::string BBS2chProxyCookieAuthManager::responseHTML()
+{
+	std::string html;
+	html += "<!DOCTYPE html><html lang=\"ja\"><head>";
+	html += "<title>proxy2ch: アカウント管理</title>";
+	html += "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">";
+	html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><style>";
+	html += ".container { width: 300px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; margin-bottom: 10px; }";
+	html += "h3 { text-align: center; margin-bottom: 20px; margin-top: 0px; }";
+	html += "input[type=\"text\"], input[type=\"password\"] { width: 93%; padding: 10px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px; }";
+	html += "button[type=\"submit\"] { width: 100%; padding: 10px; background-color: #4CAF50; color: #ffffff; border: none; border-radius: 4px; cursor: pointer; }";
+	html += "</style></head><body>";
+	html += _acornAuth.responseHTML();
+	html += _beAuth.responseHTML();
+	html += _upliftAuth.responseHTML();
 	html += "</body></html>\n";
 	return html;
 }

+ 68 - 11
BBS2chProxyAuth.h

@@ -4,6 +4,7 @@
 #include <curl/curl.h>
 #include <string>
 #include "BBS2chProxyKeyManager.h"
+#include "BBS2chProxyFormData.h"
 
 class BBS2chProxyAuth {
   private:
@@ -25,28 +26,84 @@ class BBS2chProxyAuth {
 	bool updateSID(CURL *curl);
 };
 
-class BBS2chProxyAcornAuth {
-  private:
-	std::string _cookie;
+class IBBS2chProxyCookieAuth {
+  protected:
 	time_t _lastUpdated;
 	std::string _mail;
 	std::string _passwd;
 	pthread_mutex_t _mutex;
 	bool _isUpdating;
+	std::string _headerTitle;
+	std::string _uniqueId;
+	int _updateInterval;
   public:
-	BBS2chProxyAcornAuth() : _lastUpdated(), _isUpdating() {
-		pthread_mutex_init(&_mutex, NULL);
-	};
-	BBS2chProxyAcornAuth(const std::string &mail, const std::string &passwd) : _lastUpdated(), _mail(mail), _passwd(passwd), _isUpdating() {
+	IBBS2chProxyCookieAuth() : _lastUpdated(), _isUpdating(), _updateInterval(60*60*12) {
 		pthread_mutex_init(&_mutex, NULL);
 	};
-	~BBS2chProxyAcornAuth() {
+	virtual ~IBBS2chProxyCookieAuth() {
 		pthread_mutex_destroy(&_mutex);
 	};
+	int loginWith(const std::string &mail, const std::string &passwd);
+	void prepareForLogin(CURL *curl, const std::string &authURL, BBS2chProxyFormData &requestBody);
+	void update(bool force);
+	std::string responseHTML();
+	virtual bool login() = 0;
+	virtual void logout() = 0;
+	virtual bool syncCookie(BBS2chProxyKeyManager::CookieJar &jar) = 0;
+	virtual bool isLoggedIn() = 0;
+};
+
+class BBS2chProxyAcornAuth : public IBBS2chProxyCookieAuth {
+  private:
+	std::string _cookie;
+  public:
+	BBS2chProxyAcornAuth() : IBBS2chProxyCookieAuth() {
+		_headerTitle = "どんぐりシステム";
+		_uniqueId = "donguri";
+	};
 	bool login();
-	int login(const std::string &mail, const std::string &passwd);
 	void logout();
-	void update(bool force);
-	void syncCookie(BBS2chProxyKeyManager::CookieJar &jar);
+	bool syncCookie(BBS2chProxyKeyManager::CookieJar &jar);
+	bool isLoggedIn();
+};
+
+class BBS2chProxyBeAuth : public IBBS2chProxyCookieAuth {
+  private:
+	std::string _dmdm;
+	std::string _mdmd;
+  public:
+	BBS2chProxyBeAuth() : IBBS2chProxyCookieAuth() {
+		_headerTitle = "Be";
+		_uniqueId = "be";
+	};
+	bool login();
+	void logout();
+	bool syncCookie(BBS2chProxyKeyManager::CookieJar &jar);
+	bool isLoggedIn();
+};
+
+class BBS2chProxyUpliftAuth : public IBBS2chProxyCookieAuth {
+  private:
+	std::string _cookie;
+  public:
+	BBS2chProxyUpliftAuth() : IBBS2chProxyCookieAuth() {
+		_headerTitle = "UPLIFT";
+		_uniqueId = "uplift";
+	};
+	bool login();
+	void logout();
+	bool syncCookie(BBS2chProxyKeyManager::CookieJar &jar);
+	bool isLoggedIn();
+};
+
+class BBS2chProxyCookieAuthManager {
+  private:
+	BBS2chProxyAcornAuth _acornAuth;
+	BBS2chProxyBeAuth _beAuth;
+	BBS2chProxyUpliftAuth _upliftAuth;
+  public:
+	int loginWith(const std::string &service, const std::string &mail, const std::string &passwd);
+	void logout(const std::string &service);
+	void syncCookies(BBS2chProxyKeyManager::CookieJar &jar);
 	std::string responseHTML();
 };

+ 8 - 8
BBS2chProxyConnection.cpp

@@ -76,7 +76,7 @@ BBS2chProxyKeyManager BBS2chProxyConnection::keyManager;
 BBS2chProxyAuth BBS2chProxyConnection::auth;
 BBS2chProxyBoardManager BBS2chProxyConnection::boardManager;
 BBS2chProxyConnection::AcceptEncodingChecker BBS2chProxyConnection::acceptEncodingChecker;
-BBS2chProxyAcornAuth BBS2chProxyConnection::acornAuthManager;
+BBS2chProxyCookieAuthManager BBS2chProxyConnection::cookieAuthManager;
 
 static regex_t regex;
 static regex_t regex_kako;
@@ -706,7 +706,7 @@ beginHandleRequest:
 				socketToClient->writeString(boardManager.getBoardJSON(!useHttps, use2ch, hasTalk));
 				statusCode = 200;
 			}
-			else if (requestURL.getPath() == "/5ch/donguri") {
+			else if (requestURL.getPath() == "/5ch/accounts") {
 				if (!manage_bbscgi_cookies || (curl_features & CURL_VERSION_SSL) == 0) {
 					socketToClient->sendBasicHeaders(200, "OK");
 					socketToClient->writeString("Content-Type: text/plain; charset=UTF-8\r\n\r\n");
@@ -716,7 +716,7 @@ beginHandleRequest:
 				else if (!strcasecmp(method, "GET")) {
 					socketToClient->sendBasicHeaders(200, "OK");
 					socketToClient->writeString("Content-Type: text/html; charset=UTF-8\r\n\r\n");
-					socketToClient->writeString(acornAuthManager.responseHTML());
+					socketToClient->writeString(cookieAuthManager.responseHTML());
 					statusCode = 200;
 				}
 				else if (!strcasecmp(method, "POST")) {
@@ -731,17 +731,17 @@ beginHandleRequest:
 					if (content_length > 0) {
 						BBS2chProxyFormData requestBody(postdata, content_length);
 						if (requestBody.get("action") == "login") {
-							int ret = acornAuthManager.login(requestBody.get("mail"), requestBody.get("pass"));
+							int ret = cookieAuthManager.loginWith(requestBody.get("service"), requestBody.get("mail"), requestBody.get("pass"));
 							if (ret == 0) {
 								socketToClient->sendBasicHeaders(200, "OK");
-								socketToClient->writeString("Refresh: 0; url=/5ch/donguri\r\n\r\n");
+								socketToClient->writeString("Refresh: 0; url=/5ch/accounts\r\n\r\n");
 								statusCode = 200;
 							} else if (ret < 0) {
 								socketToClient->sendBasicHeaders(200, "OK");
 								socketToClient->writeString("Content-Type: text/plain; charset=UTF-8\r\n\r\n");
 								socketToClient->writeString("ログインに失敗しました。");
 								statusCode = 200;
-							} else {
+							} else if (ret == 1) {
 								socketToClient->sendBasicHeaders(200, "OK");
 								socketToClient->writeString("Content-Type: text/plain; charset=UTF-8\r\n\r\n");
 								socketToClient->writeString("ログイン処理を現在実行中です。時間をおいて再度試してください。");
@@ -749,9 +749,9 @@ beginHandleRequest:
 							}
 						}
 						else if (requestBody.get("action") == "logout") {
-							acornAuthManager.logout();
+							cookieAuthManager.logout(requestBody.get("service"));
 							socketToClient->sendBasicHeaders(200, "OK");
-							socketToClient->writeString("Refresh: 0; url=/5ch/donguri\r\n\r\n");
+							socketToClient->writeString("Refresh: 0; url=/5ch/accounts\r\n\r\n");
 							statusCode = 200;
 						}
 					}

+ 1 - 1
BBS2chProxyConnection.h

@@ -55,7 +55,7 @@ public:
 	static BBS2chProxyAuth auth;
 	static BBS2chProxyKeyManager keyManager;
 	static AcceptEncodingChecker acceptEncodingChecker;
-	static BBS2chProxyAcornAuth acornAuthManager;
+	static BBS2chProxyCookieAuthManager cookieAuthManager;
 	
   public:
 	BBS2chProxyConnection(int socket, int port, BBS2chProxyThreadCache *cache) :

+ 9 - 6
BBS2chProxyKeyManager.cpp

@@ -365,12 +365,12 @@ bool BBS2chProxyKeyManager::saveKeys()
 #endif
 }
 
-BBS2chProxyKeyManager::CookieJar& BBS2chProxyKeyManager::getCookieJar(const std::string &userAgent, bool syncAcornCookie)
+BBS2chProxyKeyManager::CookieJar& BBS2chProxyKeyManager::getCookieJar(const std::string &userAgent, bool syncCookie)
 {
 	pthread_mutex_lock(&_mutex);
 	BBS2chProxyKeyManager::CookieJar& jar = _cookies[userAgent];
 	pthread_mutex_unlock(&_mutex);
-	if (syncAcornCookie) BBS2chProxyConnection::acornAuthManager.syncCookie(jar);
+	if (syncCookie) BBS2chProxyConnection::cookieAuthManager.syncCookies(jar);
 	return jar;
 }
 
@@ -423,10 +423,10 @@ std::string BBS2chProxyKeyManager::cookieManagerHTML()
 	html += "table { table-layout: fixed; border-collapse: collapse; width: 100%; min-width: 100ch; }";
 	html += "th { font-weight: normal; }";
 	html += "th span { font-weight: bold; }";
-	html += "tbody tr { border-top: 1px solid #ddd; }";
-	html += "thead tr { border-bottom: 2px solid #ddd; }";
-	html += "td:not(:last-child), th:not(:last-child) { border-right: 1px solid #ddd; }";
-	html += "td div { box-sizing: content-box; white-space: normal; overflow-x: hidden; word-break: break-all; max-height: 4.2em; }";
+	html += "tr { border-top: 1px solid #ddd; }";
+	html += "th:not(:last-child), td:not(:last-child) { border-right: 1px solid #ddd; }";
+	html += "thead tr, tbody tr:nth-child(even) { background-color: #eee; }";
+	html += "td div { box-sizing: content-box; white-space: normal; overflow-x: hidden; word-break: break-all; max-height: 4.2em; padding: 0 5px }";
 	html += ".expire { word-break: normal; }";
 	html += ".actions { text-align: center; }";
 	html += ".col_name, .col_doma { width: 15ch; }";
@@ -434,6 +434,7 @@ std::string BBS2chProxyKeyManager::cookieManagerHTML()
 	html += ".col_butt { width: 10ch; }";
 	html += "</style><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
 	html += "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"></head><body>";
+	bool isEmpty = true;
 	pthread_mutex_lock(&_mutex);
 	for (std::map<std::string, CookieJar>::iterator it = _cookies.begin(); it != _cookies.end(); ++it) {
 		it->second.lock();
@@ -486,8 +487,10 @@ std::string BBS2chProxyKeyManager::cookieManagerHTML()
 		}
 		it->second.unlock();
 		html += "</tbody></table></div>";
+		isEmpty = false;
 	}
 	pthread_mutex_unlock(&_mutex);
+	if (isEmpty) html += "proxy2chが管理しているクッキーはありません。";
 	html += "</body></html>";
 	return html;
 }

+ 4 - 2
BBS2chProxyKeyManager.h

@@ -28,7 +28,9 @@ public:
 		pthread_mutex_t _mutex;
 	public:
 		time_t timeAcornSynced;
-		CookieJar() : timeAcornSynced() {
+		time_t timeBeSynced;
+		time_t timeUpliftSynced;
+		CookieJar() : timeAcornSynced(), timeBeSynced(), timeUpliftSynced() {
 			pthread_mutex_init(&_mutex, NULL);
 		};
 		~CookieJar() {
@@ -64,7 +66,7 @@ public:
 	double secondsToWaitBeforePosting(const std::string &key);
 	void setStorage(const char *jsonPath);
 	int loadKeys();
-	CookieJar& getCookieJar(const std::string &userAgent, bool syncAcornCookie=false);
+	CookieJar& getCookieJar(const std::string &userAgent, bool syncCookie=false);
 	bool flushCookies();
 	std::string cookieManagerHTML();
 private:

+ 25 - 7
BBS2chProxyPoster.cpp

@@ -623,6 +623,27 @@ curl_slist* IBBS2chProxyPoster::prepareCurlHandle(BBS2chProxyHttpHeaders &header
 		log_printf(1, "Cookies are managed by proxy2ch, most of existing \"Cookie: \" headers are ignored.\n");
 		curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); //enable cookie engine explicitly
 		curl_easy_setopt(curl, CURLOPT_COOKIELIST, "ALL");  //erase all cookies explicitly
+		bool hasBeCookieInJar = false;
+		bool hasUpliftSidCookieInJar = false;
+		BBS2chProxyKeyManager::CookieJar &jar = BBS2chProxyConnection::keyManager.getCookieJar(_userAgentForRequest, true);
+		jar.lock();
+		std::vector<BBS2chProxyKeyManager::Cookie> &list = jar.getList();
+		for (std::vector<BBS2chProxyKeyManager::Cookie>::iterator it = list.begin(); it != list.end(); ++it) {
+			if (!it->isExpired()) {
+				curl_easy_setopt(curl, CURLOPT_COOKIELIST, it->valueInNetscapeFormat().c_str());
+				if (!hasBeCookieInJar && (it->name == "DMDM" || it->name == "MDMD") && it->domain == ".5ch.net") {
+					if (it->domain.size() <= _host.size() && _host.find(it->domain, _host.size()-it->domain.size()) != std::string::npos) {
+						hasBeCookieInJar = true;
+					}
+				}
+				if (!hasUpliftSidCookieInJar && it->name == "sid" && (it->domain == ".5ch.net" || it->domain == ".bbspink.com")) {
+					if (it->domain.size() <= _host.size() && _host.find(it->domain, _host.size()-it->domain.size()) != std::string::npos) {
+						hasUpliftSidCookieInJar = true;
+					}
+				}
+			}
+		}
+		jar.unlock();
 		if (headers.has("Cookie")) {
 			const std::string &value = headers.getEntry("Cookie")->getValue();
 			std::vector<std::string> list;
@@ -641,7 +662,7 @@ curl_slist* IBBS2chProxyPoster::prepareCurlHandle(BBS2chProxyHttpHeaders &header
 				size_t pos = it->find('=');
 				if (pos == std::string::npos) continue;
 				std::string name = it->substr(0, pos);
-				if (name == "DMDM" || name == "MDMD" || name == "sid") {
+				if ((!hasUpliftSidCookieInJar && name == "sid") || (!hasBeCookieInJar && (name == "DMDM" || name == "MDMD"))) {
 					if (newCookie.size()) newCookie += "; ";
 					newCookie += *it;
 				}
@@ -649,13 +670,10 @@ curl_slist* IBBS2chProxyPoster::prepareCurlHandle(BBS2chProxyHttpHeaders &header
 			if (newCookie.size()) curl_easy_setopt(curl, CURLOPT_COOKIE, newCookie.c_str());
 			headers.remove("Cookie");
 		}
-		BBS2chProxyKeyManager::CookieJar &jar = BBS2chProxyConnection::keyManager.getCookieJar(_userAgentForRequest, true);
-		jar.lock();
-		std::vector<BBS2chProxyKeyManager::Cookie> &list = jar.getList();
-		for (std::vector<BBS2chProxyKeyManager::Cookie>::iterator it = list.begin(); it != list.end(); ++it) {
-			if (!it->isExpired()) curl_easy_setopt(curl, CURLOPT_COOKIELIST, it->valueInNetscapeFormat().c_str());
+		if (hasUpliftSidCookieInJar) {
+			headers.remove("X-Ronin-Sid");
+			body.remove("sid");
 		}
-		jar.unlock();
 	}
 #endif
 	headersForCurl = headers.appendToCurlSlist(headersForCurl);