boinc/clientgui/browser.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;
}