mirror of https://github.com/BOINC/boinc.git
517 lines
14 KiB
C
517 lines
14 KiB
C
// Berkeley Open Infrastructure for Network Computing
|
|
// http://boinc.berkeley.edu
|
|
// Copyright (C) 2005 University of California
|
|
//
|
|
// This is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation;
|
|
// either version 2.1 of the License, or (at your option) any later version.
|
|
//
|
|
// This software is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU Lesser General Public License for more details.
|
|
//
|
|
// To view the GNU Lesser General Public License visit
|
|
// http://www.gnu.org/copyleft/lesser.html
|
|
// or write to the Free Software Foundation, Inc.,
|
|
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
#if defined(_WIN32) && !defined(__STDWX_H__) && !defined(_BOINC_WIN_) && !defined(_AFX_STDAFX_H_)
|
|
#include "boinc_win.h"
|
|
#endif
|
|
|
|
#ifndef _WIN32
|
|
#include <string>
|
|
#include <vector>
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#include "error_numbers.h"
|
|
#include "mfile.h"
|
|
#include "miofile.h"
|
|
#include "str_util.h"
|
|
#include "browser.h"
|
|
|
|
|
|
//
|
|
// Utility Functions
|
|
//
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Prototype for SHGetFolderPath() in shell32.dll.
|
|
typedef HRESULT (WINAPI *MYSHGETFOLDERPATH)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPSTR pszPath);
|
|
|
|
#endif
|
|
|
|
// retrieve the user's application data directory.
|
|
// Win : C:\Documents and Settings\<username>\Application Data
|
|
// Unix: ~/
|
|
//
|
|
void get_home_dir_path( std::string& path ) {
|
|
#ifdef _WIN32
|
|
TCHAR szBuffer[MAX_PATH];
|
|
HMODULE hShell32;
|
|
MYSHGETFOLDERPATH pfnMySHGetFolderPath = NULL;
|
|
|
|
// Attempt to link to dynamic function if it exists
|
|
hShell32 = LoadLibrary(_T("SHELL32.DLL"));
|
|
if (NULL != hShell32) {
|
|
pfnMySHGetFolderPath = (MYSHGETFOLDERPATH) GetProcAddress(hShell32, _T("SHGetFolderPathA"));
|
|
}
|
|
|
|
if (NULL != pfnMySHGetFolderPath) {
|
|
if (SUCCEEDED((pfnMySHGetFolderPath)(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, szBuffer))) {
|
|
path = std::string(szBuffer);
|
|
path += std::string("\\");
|
|
}
|
|
}
|
|
|
|
// Free the dynamically linked to library
|
|
if (NULL != hShell32) {
|
|
FreeLibrary(hShell32);
|
|
}
|
|
#elif defined(__APPLE__)
|
|
path = std::string(getenv("HOME") )+ std::string("/");
|
|
#else
|
|
path = std::string("~/");
|
|
#endif
|
|
}
|
|
|
|
// parse name value pairs based on INI file rules.
|
|
bool parse_name_value_pair(char* buf, std::string& name, std::string& value) {
|
|
std::basic_string<char>::size_type i;
|
|
std::string s;
|
|
|
|
s = std::string(buf);
|
|
i = s.find("=", 0);
|
|
if ( i < s.npos ) {
|
|
name = s.substr(0, i);
|
|
value = s.substr(i + 1);
|
|
strip_whitespace(name);
|
|
strip_whitespace(value);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// parse host name from url.
|
|
bool parse_hostname(std::string& project_url, std::string& hostname) {
|
|
std::basic_string<char>::size_type start;
|
|
std::basic_string<char>::size_type end;
|
|
|
|
start = project_url.find("//", 0) + 2;
|
|
end = project_url.find("/", start);
|
|
|
|
hostname = project_url.substr(start, end - start);
|
|
if (starts_with(hostname.c_str(), "www"))
|
|
hostname.erase(0, 3);
|
|
if (!hostname.empty())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
// is the authenticator valid?
|
|
bool is_authenticator_valid(const std::string authenticator) {
|
|
std::string tmp = authenticator;
|
|
|
|
// Perform some basic validation of the suspect authenticator
|
|
//
|
|
|
|
// If the string is null then it is invalid.
|
|
if (0 == tmp.length()) {
|
|
return false;
|
|
}
|
|
|
|
// If the string contains non alpha numeric characters it is invalid.
|
|
std::string::iterator it = tmp.begin();
|
|
while (it != tmp.end()) {
|
|
if (!isalpha(*it) && !isdigit(*it)) {
|
|
return false;
|
|
}
|
|
it++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Mozilla-Based Browser Support
|
|
//
|
|
class MOZILLA_PROFILE {
|
|
public:
|
|
std::string name;
|
|
std::string path;
|
|
bool is_relative;
|
|
bool is_default;
|
|
|
|
MOZILLA_PROFILE();
|
|
|
|
void clear();
|
|
int parse(MIOFILE& in);
|
|
};
|
|
|
|
|
|
class MOZILLA_PROFILES {
|
|
public:
|
|
std::vector<MOZILLA_PROFILE*> profiles;
|
|
|
|
MOZILLA_PROFILES();
|
|
|
|
void clear();
|
|
int parse(MIOFILE& in);
|
|
};
|
|
|
|
|
|
MOZILLA_PROFILE::MOZILLA_PROFILE() {
|
|
clear();
|
|
}
|
|
|
|
|
|
void MOZILLA_PROFILE::clear() {
|
|
name.clear();
|
|
path.clear();
|
|
is_relative = false;
|
|
is_default = false;
|
|
}
|
|
|
|
|
|
int MOZILLA_PROFILE::parse(MIOFILE& in) {
|
|
char buf[512];
|
|
std::string sn;
|
|
std::string sv;
|
|
|
|
while (in.fgets(buf, 512)) {
|
|
if (starts_with(buf, "\n")) return 0;
|
|
if (starts_with(buf, "\r\n")) return 0;
|
|
if (!parse_name_value_pair(buf, sn, sv)) continue;
|
|
|
|
if ("Name" == sn) name = sv;
|
|
if ("Path" == sn) path = sv;
|
|
if (("IsRelative" == sn) && ("1" == sv)) is_relative = true;
|
|
if (("Default" == sn) && ("1" == sv)) is_default = true;
|
|
}
|
|
return ERR_FREAD;
|
|
}
|
|
|
|
|
|
MOZILLA_PROFILES::MOZILLA_PROFILES() {
|
|
clear();
|
|
}
|
|
|
|
|
|
void MOZILLA_PROFILES::clear() {
|
|
unsigned int i;
|
|
for (i=0; i<profiles.size(); i++) {
|
|
delete profiles[i];
|
|
}
|
|
profiles.clear();
|
|
}
|
|
|
|
|
|
int MOZILLA_PROFILES::parse(MIOFILE& in) {
|
|
char buf[512];
|
|
MOZILLA_PROFILE* p = NULL;
|
|
|
|
while (in.fgets(buf, 512)) {
|
|
if (starts_with(buf, "[Profile")) {
|
|
p = new MOZILLA_PROFILE;
|
|
if (!p->parse( in )) {
|
|
profiles.push_back( p );
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// search for the project specific 'Setup' cookie for mozilla based
|
|
// browsers.
|
|
//
|
|
// file format is:
|
|
//
|
|
// host \t isDomain \t path \t secure \t expires \t name \t cookie
|
|
//
|
|
// if this format isn't respected we move onto the next line in the file.
|
|
// isDomain is "TRUE" or "FALSE" (default to "FALSE")
|
|
// isSecure is "TRUE" or "FALSE" (default to "TRUE")
|
|
// expires is a PRInt64 integer
|
|
// note 1: cookie can contain tabs.
|
|
// note 2: cookies are written in order of lastAccessed time:
|
|
// most-recently used come first; least-recently-used come last.
|
|
//
|
|
bool find_project_cookie_mozilla_generic(
|
|
MIOFILE& in, std::string& project_url, std::string& authenticator
|
|
) {
|
|
bool retval = false;
|
|
char buf[2048];
|
|
char host[256], domain[16], path[256], secure[16], name[256], cookie[256];
|
|
long long expires;
|
|
std::string hostname;
|
|
|
|
|
|
strcpy(host, "");
|
|
strcpy(domain, "");
|
|
strcpy(path, "");
|
|
strcpy(secure, "");
|
|
strcpy(name, "");
|
|
strcpy(cookie, "");
|
|
expires = 0;
|
|
|
|
|
|
// determine the project hostname using the project url
|
|
parse_hostname(project_url, hostname);
|
|
|
|
// traverse cookie file
|
|
while (in.fgets(buf, 2048)) {
|
|
sscanf(
|
|
buf,
|
|
#ifdef _WIN32
|
|
"%255s\t%15s\t%255s\t%15s\t%I64d\t%255s\t%255s",
|
|
#else
|
|
"%255s\t%15s\t%255s\t%15s\t%Ld\t%255s\t%255s",
|
|
#endif
|
|
host, domain, path, secure, &expires, name, cookie
|
|
);
|
|
|
|
// is this a real cookie?
|
|
// temporary cookie? these cookies do not trickle back up
|
|
// to the jscript interface, so ignore them.
|
|
if (starts_with(host, "#HttpOnly")) continue;
|
|
|
|
// is this the right host?
|
|
if (!strstr(host, hostname.c_str())) continue;
|
|
|
|
// has the cookie expired?
|
|
if (time(0) > expires) continue;
|
|
|
|
// is this the right cookie?
|
|
if (starts_with(name, "Setup")) {
|
|
// If validation failed, null out the authenticator just in case
|
|
// somebody tries to use it, otherwise copy in the real deal.
|
|
if (!is_authenticator_valid(cookie)) {
|
|
authenticator = "";
|
|
} else {
|
|
authenticator = cookie;
|
|
retval = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
// traverse the profiles and determine which cookie fle to use.
|
|
// this should be compatible with firefox, seamonkey, and netscape.
|
|
//
|
|
bool detect_setup_authenticator_mozilla_generic(
|
|
std::string profiles_root, std::string& project_url, std::string& authenticator
|
|
) {
|
|
bool retval = false;
|
|
FILE* pf = NULL;
|
|
FILE* cf = NULL;
|
|
MIOFILE pmf;
|
|
MIOFILE cmf;
|
|
MOZILLA_PROFILES mps;
|
|
MOZILLA_PROFILE* mp = NULL;
|
|
std::string cookies_root;
|
|
std::string tmp;
|
|
unsigned int i = 0;
|
|
unsigned int default_index = 0;
|
|
|
|
// lets see if we can open the profiles configuration file
|
|
tmp = profiles_root + "profiles.ini";
|
|
pf = fopen(tmp.c_str(), "r");
|
|
|
|
// if profiles configuration file exists, parse it.
|
|
if (pf) {
|
|
pmf.init_file(pf);
|
|
mps.parse(pmf);
|
|
}
|
|
|
|
// we need to know which cookie file to look at, so if only
|
|
// one profile exists, use it.
|
|
//
|
|
// if more than one profile exists, look through all the
|
|
// profiles until we find the default profile. even when the
|
|
// user selects a different profile at startup the default
|
|
// profile flag is changed at startup to the new profile.
|
|
if (mps.profiles.size() == 0) {
|
|
return retval; // something is very wrong, don't
|
|
// risk a crash
|
|
}
|
|
|
|
if (mps.profiles.size() == 1) {
|
|
default_index = 0;
|
|
} else {
|
|
for (i=0; i<mps.profiles.size(); i++) {
|
|
if (mps.profiles[i]->is_default) {
|
|
default_index = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// should the path be treated as an absolute path or a relative
|
|
// path?
|
|
mp = mps.profiles[default_index];
|
|
if (mp->is_relative) {
|
|
cookies_root = profiles_root + mp->path + "/";
|
|
} else {
|
|
cookies_root = mp->path + "/";
|
|
}
|
|
|
|
// now we should open up the cookie file.
|
|
tmp = cookies_root + "cookies.txt";
|
|
cf = fopen(tmp.c_str(), "r");
|
|
|
|
// if the cookie file exists, lookup the projects 'Setup' cookie.
|
|
if (cf) {
|
|
cmf.init_file(cf);
|
|
retval = find_project_cookie_mozilla_generic(
|
|
cmf,
|
|
project_url,
|
|
authenticator
|
|
);
|
|
}
|
|
|
|
// cleanup
|
|
if (cf) fclose(pf);
|
|
if (pf) fclose(pf);
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
//
|
|
// Firefox Browser Support
|
|
//
|
|
|
|
bool get_firefox_profiles_root( std::string& profiles_root ) {
|
|
get_home_dir_path( profiles_root );
|
|
#ifdef _WIN32
|
|
profiles_root += std::string("Mozilla\\Firefox\\");
|
|
#elif defined(__APPLE__)
|
|
profiles_root += std::string("Library/Application Support/Firefox/");
|
|
#else
|
|
profiles_root += std::string(".mozilla/firefox/");
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
|
|
bool detect_setup_authenticator_firefox(
|
|
std::string& project_url, std::string& authenticator
|
|
) {
|
|
std::string profiles_root;
|
|
get_firefox_profiles_root(profiles_root);
|
|
return detect_setup_authenticator_mozilla_generic(
|
|
profiles_root,
|
|
project_url,
|
|
authenticator
|
|
);
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
//
|
|
// Internet Explorer Browser Support
|
|
//
|
|
|
|
//
|
|
// Detect the 'Setup' cookie in Internet Explorer by using the InternetGetCookie API.
|
|
//
|
|
bool detect_setup_authenticator_ie(std::string& project_url, std::string& authenticator)
|
|
{
|
|
bool bReturnValue = false;
|
|
char szCookieBuffer[2048];
|
|
char* pszCookieFragment = NULL;
|
|
DWORD dwSize = sizeof(szCookieBuffer)/sizeof(char);
|
|
std::string strCookieFragment;
|
|
std::string strCookieName;
|
|
std::string strCookieValue;
|
|
size_t uiDelimeterLocation;
|
|
std::string hostname;
|
|
|
|
|
|
// if we don't find the cookie at the exact project dns name, check one higher (i.e. www.worldcommunitygrid.org becomes
|
|
// worldcommunitygrid.org
|
|
parse_hostname(project_url, hostname);
|
|
|
|
bReturnValue = InternetGetCookie(project_url.c_str(), NULL, szCookieBuffer, &dwSize) == TRUE;
|
|
if (!bReturnValue) bReturnValue = InternetGetCookie(hostname.c_str(), NULL, szCookieBuffer, &dwSize) == TRUE;
|
|
if (bReturnValue)
|
|
{
|
|
// reset this becuase at this point we just know that we found some cookies for the website. We don't
|
|
// know if we actually found the Setup cookie
|
|
//
|
|
bReturnValue = false;
|
|
// Format of cookie buffer:
|
|
// 'cookie1=value1; cookie2=value2; cookie3=value3;
|
|
//
|
|
pszCookieFragment = strtok(szCookieBuffer, "; ");
|
|
while(pszCookieFragment)
|
|
{
|
|
// Convert to a std::string so we can go to town
|
|
strCookieFragment = pszCookieFragment;
|
|
|
|
// Extract the name & value
|
|
uiDelimeterLocation = strCookieFragment.find("=", 0);
|
|
strCookieName = strCookieFragment.substr(0, uiDelimeterLocation);
|
|
strCookieValue = strCookieFragment.substr(uiDelimeterLocation + 1);
|
|
|
|
if (std::string("Setup") == strCookieName)
|
|
{
|
|
// If validation failed, null out the authenticator just in case
|
|
// somebody tries to use it, otherwise copy in the real deal.
|
|
if (!is_authenticator_valid(strCookieValue)) {
|
|
authenticator = "";
|
|
} else {
|
|
// Now we found it! Yea - auto attach!
|
|
authenticator = strCookieValue;
|
|
bReturnValue = true;
|
|
}
|
|
}
|
|
|
|
pszCookieFragment = strtok(NULL, "; ");
|
|
}
|
|
}
|
|
|
|
return bReturnValue;
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// walk through the various browsers looking up the
|
|
// project cookies until the projects 'Setup' cookie is found.
|
|
//
|
|
// give preference to the default platform specific browers first before going
|
|
// to the platform independant browsers since most people don't switch from
|
|
// the default.
|
|
//
|
|
bool detect_setup_authenticator(
|
|
std::string& project_url, std::string& authenticator
|
|
) {
|
|
#ifdef _WIN32
|
|
if (detect_setup_authenticator_ie(project_url, authenticator)) {
|
|
return true;
|
|
}
|
|
#endif
|
|
#ifdef __APPLE__
|
|
if (detect_setup_authenticator_safari(project_url, authenticator)) {
|
|
return true;
|
|
}
|
|
#endif
|
|
if (detect_setup_authenticator_firefox(project_url, authenticator)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|