From e3cf86cc7677df7837e804089d7f73fce19cd4f5 Mon Sep 17 00:00:00 2001 From: Cooper Quintin Date: Tue, 12 Aug 2014 10:31:54 -0700 Subject: [PATCH 01/54] adding python library to generate Olson to Posix timezone dictionary --- routerapi/genTzDictionary.py | 85 +++++++ routerapi/tz_info.py | 423 +++++++++++++++++++++++++++++++++++ 2 files changed, 508 insertions(+) create mode 100644 routerapi/genTzDictionary.py create mode 100644 routerapi/tz_info.py diff --git a/routerapi/genTzDictionary.py b/routerapi/genTzDictionary.py new file mode 100644 index 0000000..50c6af9 --- /dev/null +++ b/routerapi/genTzDictionary.py @@ -0,0 +1,85 @@ +#!/usr/bin/python +""" +genTzDictionary - Generate a python file containing a dictionary that maps +Olson Time Zones to their POSIX equivalents. +""" + +import os +import sys +import re + +TZ_DB = "/usr/share/zoneinfo" +PY_TZ_FILE_PATH = "./tz_info.py" + + +def generate_dictionary_file(): + tz_info = get_zone_info() + if tz_info: + write_zone_file(get_zone_info()) + exit(0) + + #exit with error status if we couldn't read zone info + exit(1) + + +def get_zone_info(): + """ + get_zone_info - parse zone info from zoneinfo database into + a python dictionary + """ + tzd = {} + try: + with open(os.path.join(TZ_DB, "zone.tab"), "r") as zonetab: + for line in zonetab: + #skip comments and whitespace + if re.match("^#", line) or re.match("^\s+$", line): + continue + + #olson zone paths are the third column in the table + sep = re.compile("\s+") + zone = sep.split(line)[2] + + try: + tzd[zone] = read_posix_zone(zone) + except EnvironmentError: #IOError or OSError + continue + + return tzd + + except EnvironmentError: + return false + + +def read_posix_zone(zone): + """ + read_posix_zone - return the posix time zone for a give olson time zone + @param string zone - olson time zone string + """ + with open(os.path.join(TZ_DB, zone), "r") as zonefile: + #return last line of file + return list(zonefile)[-1].rstrip() + + +def write_zone_file(tz_dict): + """ + write_zone_file - write a timezone dictionary to a new python file + @param dictionary tz_info - a dictionary mapping olson timezones to POSIX + equivalents + """ + with open(PY_TZ_FILE_PATH, 'w') as py_file: + header = ("#!/usr/bin/python\n" + "\"\"\"\ntz_info - maps Olson time zones to POSIX equivalents\nAutogenerated by genTzDictionary.py\n\"\"\"\n" + "tz_info = {\n") + + footer = "}\n" + + py_file.write(header) + + for otz in tz_dict: + py_file.write(" \"{0}\": \"{1}\",\n".format(otz,tz_dict[otz])) + + py_file.write(footer) + + +if __name__ == '__main__': + generate_dictionary_file() diff --git a/routerapi/tz_info.py b/routerapi/tz_info.py new file mode 100644 index 0000000..bd9037f --- /dev/null +++ b/routerapi/tz_info.py @@ -0,0 +1,423 @@ +#!/usr/bin/python +""" +tz_info - maps Olson time zones to POSIX equivalents +Autogenerated by genTzDictionary.py +""" +tz_info = { + "Atlantic/Canary": "WET0WEST,M3.5.0/1,M10.5.0", + "Australia/Melbourne": "EST-10EST,M10.1.0,M4.1.0/3", + "Europe/Minsk": "FET-3", + "America/Nipigon": "EST5EDT,M3.2.0,M11.1.0", + "America/Miquelon": "PMST3PMDT,M3.2.0,M11.1.0", + "Pacific/Wallis": "WFT-12", + "Antarctica/Davis": "DAVT-7", + "Asia/Dhaka": "BDT-6", + "America/St_Lucia": "AST4", + "Asia/Kashgar": "CST-8", + "America/Phoenix": "MST7", + "Asia/Kuwait": "AST-3", + "Asia/Hong_Kong": "HKT-8", + "Arctic/Longyearbyen": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Guernsey": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Paris": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Stockholm": "CET-1CEST,M3.5.0,M10.5.0/3", + "Pacific/Fiji": "FJT-12FJST,M10.3.1/146,M1.3.4/74", + "Pacific/Apia": "WST-13WSDT,M9.5.0/3,M4.1.0/4", + "Pacific/Pago_Pago": "SST11", + "Asia/Rangoon": "MMT-6:30", + "America/Mexico_City": "CST6CDT,M4.1.0,M10.5.0", + "America/Puerto_Rico": "AST4", + "Indian/Mauritius": "MUT-4", + "Europe/Berlin": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Zurich": "CET-1CEST,M3.5.0,M10.5.0/3", + "America/Belem": "BRT3", + "Antarctica/Macquarie": "MIST-11", + "Asia/Krasnoyarsk": "KRAT-8", + "Atlantic/Bermuda": "AST4ADT,M3.2.0,M11.1.0", + "Australia/Currie": "EST-10EST,M10.1.0,M4.1.0/3", + "Asia/Tehran": "", + "Asia/Baku": "AZT-4AZST,M3.5.0/4,M10.5.0/5", + "America/St_Barthelemy": "AST4", + "America/Santarem": "BRT3", + "America/Danmarkshavn": "GMT0", + "America/Scoresbysund": "EGT1EGST,M3.5.0/0,M10.5.0/1", + "America/Eirunepe": "ACT5", + "America/Caracas": "VET4:30", + "Asia/Baghdad": "AST-3", + "Africa/Monrovia": "GMT0", + "America/St_Vincent": "AST4", + "America/Vancouver": "PST8PDT,M3.2.0,M11.1.0", + "Asia/Ho_Chi_Minh": "ICT-7", + "Europe/Busingen": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Thimphu": "BTT-6", + "Africa/Accra": "GMT0", + "America/Belize": "CST6", + "America/Edmonton": "MST7MDT,M3.2.0,M11.1.0", + "Asia/Tashkent": "UZT-5", + "Asia/Tokyo": "JST-9", + "Pacific/Kiritimati": "LINT-14", + "Australia/Sydney": "EST-10EST,M10.1.0,M4.1.0/3", + "Europe/Riga": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Asia/Dili": "TLT-9", + "Africa/Mbabane": "SAST-2", + "Asia/Oral": "ORAT-5", + "Asia/Aden": "AST-3", + "Europe/Isle_of_Man": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Istanbul": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Asia/Magadan": "MAGT-12", + "Australia/Lindeman": "EST-10", + "Pacific/Galapagos": "GALT6", + "America/Bogota": "COT5", + "Africa/Asmara": "EAT-3", + "America/Chicago": "CST6CDT,M3.2.0,M11.1.0", + "Pacific/Kwajalein": "MHT-12", + "Australia/Broken_Hill": "CST-9:30CST,M10.1.0,M4.1.0/3", + "America/Cuiaba": "AMT4AMST,M10.3.0/0,M2.3.0/0", + "Indian/Christmas": "CXT-7", + "Asia/Jayapura": "WIT-9", + "Europe/Brussels": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Lisbon": "WET0WEST,M3.5.0/1,M10.5.0", + "Asia/Chongqing": "CST-8", + "America/Argentina/Cordoba": "ART3", + "America/Noronha": "FNT2", + "Europe/Podgorica": "CET-1CEST,M3.5.0,M10.5.0/3", + "Africa/Algiers": "CET-1", + "Africa/Harare": "CAT-2", + "Africa/Ndjamena": "WAT-1", + "America/Costa_Rica": "CST6", + "Europe/Ljubljana": "CET-1CEST,M3.5.0,M10.5.0/3", + "Indian/Mayotte": "EAT-3", + "Asia/Phnom_Penh": "ICT-7", + "America/Managua": "CST6", + "Asia/Brunei": "BNT-8", + "America/Tijuana": "PST8PDT,M3.2.0,M11.1.0", + "Pacific/Fakaofo": "TKT-13", + "America/Martinique": "AST4", + "America/Antigua": "AST4", + "America/Indiana/Indianapolis": "EST5EDT,M3.2.0,M11.1.0", + "America/Argentina/La_Rioja": "ART3", + "Pacific/Tahiti": "TAHT10", + "America/Pangnirtung": "EST5EDT,M3.2.0,M11.1.0", + "Europe/Zagreb": "CET-1CEST,M3.5.0,M10.5.0/3", + "America/Asuncion": "PYT4PYST,M10.1.0/0,M3.4.0/0", + "Europe/Vienna": "CET-1CEST,M3.5.0,M10.5.0/3", + "Australia/Hobart": "EST-10EST,M10.1.0,M4.1.0/3", + "America/Juneau": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Inuvik": "MST7MDT,M3.2.0,M11.1.0", + "America/Ojinaga": "MST7MDT,M3.2.0,M11.1.0", + "Asia/Seoul": "KST-9", + "Indian/Comoro": "EAT-3", + "Antarctica/Rothera": "ROTT3", + "Europe/Tallinn": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Indian/Mahe": "SCT-4", + "America/Argentina/Jujuy": "ART3", + "America/Creston": "MST7", + "America/Adak": "HAST10HADT,M3.2.0,M11.1.0", + "Asia/Singapore": "SGT-8", + "Africa/Nairobi": "EAT-3", + "America/Maceio": "BRT3", + "Asia/Urumqi": "CST-8", + "Europe/Moscow": "MSK-4", + "Asia/Pyongyang": "KST-9", + "Asia/Ulaanbaatar": "ULAT-8", + "America/Rainy_River": "CST6CDT,M3.2.0,M11.1.0", + "Indian/Maldives": "MVT-5", + "Asia/Colombo": "IST-5:30", + "Australia/Adelaide": "CST-9:30CST,M10.1.0,M4.1.0/3", + "America/Cambridge_Bay": "MST7MDT,M3.2.0,M11.1.0", + "Africa/Luanda": "WAT-1", + "Pacific/Chatham": "CHAST-12:45CHADT,M9.5.0/2:45,M4.1.0/3:45", + "America/Indiana/Winamac": "EST5EDT,M3.2.0,M11.1.0", + "Asia/Tbilisi": "GET-4", + "Europe/Gibraltar": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Karachi": "PKT-5", + "Asia/Harbin": "CST-8", + "Australia/Lord_Howe": "LHST-10:30LHST-11,M10.1.0,M4.1.0", + "America/Bahia_Banderas": "CST6CDT,M4.1.0,M10.5.0", + "America/Boa_Vista": "AMT4", + "Africa/Tripoli": "EET-2", + "Indian/Reunion": "RET-4", + "Atlantic/Stanley": "FKST3", + "America/Blanc-Sablon": "AST4", + "Antarctica/Syowa": "SYOT-3", + "America/Jamaica": "EST5", + "Europe/Kiev": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Budapest": "CET-1CEST,M3.5.0,M10.5.0/3", + "Pacific/Midway": "SST11", + "America/Goose_Bay": "AST4ADT,M3.2.0,M11.1.0", + "Asia/Amman": "EET-2EEST,M3.5.4/24,M10.5.5/1", + "Asia/Sakhalin": "SAKT-11", + "Africa/Windhoek": "WAT-1WAST,M9.1.0,M4.1.0", + "America/Sitka": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Guyana": "GYT4", + "Pacific/Pohnpei": "PONT-11", + "America/Sao_Paulo": "BRT3BRST,M10.3.0/0,M2.3.0/0", + "America/Lower_Princes": "AST4", + "Australia/Perth": "WST-8", + "Africa/Djibouti": "EAT-3", + "Asia/Jakarta": "WIB-7", + "Antarctica/Palmer": "CLT4CLST,M9.1.6/24,M4.4.6/24", + "Africa/Johannesburg": "SAST-2", + "Asia/Irkutsk": "IRKT-9", + "Africa/Niamey": "WAT-1", + "Africa/Casablanca": "WET0WEST,M3.5.0,M10.5.0/3", + "America/Indiana/Marengo": "EST5EDT,M3.2.0,M11.1.0", + "Africa/Nouakchott": "GMT0", + "Europe/Vilnius": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "America/Cayenne": "GFT3", + "Africa/Mogadishu": "EAT-3", + "America/Kentucky/Monticello": "EST5EDT,M3.2.0,M11.1.0", + "America/Rio_Branco": "ACT5", + "America/Cancun": "CST6CDT,M4.1.0,M10.5.0", + "America/Indiana/Knox": "CST6CDT,M3.2.0,M11.1.0", + "America/Havana": "CST5CDT,M3.2.0/0,M11.1.0/1", + "Pacific/Guam": "ChST-10", + "Pacific/Kosrae": "KOST-11", + "Atlantic/Azores": "AZOT1AZOST,M3.5.0/0,M10.5.0/1", + "Australia/Eucla": "CWST-8:45", + "Asia/Shanghai": "CST-8", + "America/Rankin_Inlet": "CST6CDT,M3.2.0,M11.1.0", + "Asia/Beirut": "EET-2EEST,M3.5.0/0,M10.5.0/0", + "Africa/Maputo": "CAT-2", + "Asia/Bahrain": "AST-3", + "Asia/Ashgabat": "TMT-5", + "Asia/Riyadh": "AST-3", + "Europe/London": "GMT0BST,M3.5.0/1,M10.5.0", + "America/Monterrey": "CST6CDT,M4.1.0,M10.5.0", + "America/Anguilla": "AST4", + "Asia/Damascus": "EET-2EEST,M3.5.5/0,M10.5.5/0", + "America/North_Dakota/Center": "CST6CDT,M3.2.0,M11.1.0", + "America/Indiana/Vevay": "EST5EDT,M3.2.0,M11.1.0", + "Atlantic/St_Helena": "GMT0", + "America/Barbados": "AST4", + "Europe/Vatican": "CET-1CEST,M3.5.0,M10.5.0/3", + "Atlantic/Faroe": "WET0WEST,M3.5.0/1,M10.5.0", + "Asia/Almaty": "ALMT-6", + "America/Santo_Domingo": "AST4", + "Africa/Brazzaville": "WAT-1", + "America/Nome": "AKST9AKDT,M3.2.0,M11.1.0", + "Europe/Dublin": "GMT0IST,M3.5.0/1,M10.5.0", + "America/Yakutat": "AKST9AKDT,M3.2.0,M11.1.0", + "America/Argentina/Mendoza": "ART3", + "America/Araguaina": "BRT3", + "Europe/Vaduz": "CET-1CEST,M3.5.0,M10.5.0/3", + "Antarctica/Mawson": "MAWT-5", + "Asia/Kolkata": "IST-5:30", + "Africa/Maseru": "SAST-2", + "America/Atikokan": "EST5", + "America/Santa_Isabel": "PST8PDT,M4.1.0,M10.5.0", + "Asia/Kuching": "MYT-8", + "Africa/Libreville": "WAT-1", + "Africa/Freetown": "GMT0", + "Africa/Bissau": "GMT0", + "Europe/Samara": "SAMT-4", + "Europe/Amsterdam": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Tirane": "CET-1CEST,M3.5.0,M10.5.0/3", + "Pacific/Saipan": "ChST-10", + "Africa/Abidjan": "GMT0", + "Europe/Zaporozhye": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "America/El_Salvador": "CST6", + "Europe/Madrid": "CET-1CEST,M3.5.0,M10.5.0/3", + "Africa/Juba": "EAT-3", + "America/Santiago": "CLT4CLST,M9.1.6/24,M4.4.6/24", + "America/Argentina/Buenos_Aires": "ART3", + "America/Argentina/San_Luis": "ART3", + "Europe/Skopje": "CET-1CEST,M3.5.0,M10.5.0/3", + "America/Aruba": "AST4", + "America/Regina": "CST6", + "Pacific/Chuuk": "CHUT-10", + "Asia/Khandyga": "YAKT-10", + "Pacific/Funafuti": "TVT-12", + "America/Merida": "CST6CDT,M4.1.0,M10.5.0", + "America/Guatemala": "CST6", + "Africa/Lome": "GMT0", + "Africa/Sao_Tome": "GMT0", + "Asia/Makassar": "WITA-8", + "Africa/Bujumbura": "CAT-2", + "Europe/Chisinau": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Europe/Warsaw": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Yekaterinburg": "YEKT-6", + "Antarctica/Casey": "WST-8", + "America/Halifax": "AST4ADT,M3.2.0,M11.1.0", + "America/Thule": "AST4ADT,M3.2.0,M11.1.0", + "America/St_Johns": "NST3:30NDT,M3.2.0,M11.1.0", + "America/Moncton": "AST4ADT,M3.2.0,M11.1.0", + "Europe/Helsinki": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Atlantic/Cape_Verde": "CVT1", + "America/Tegucigalpa": "CST6", + "Indian/Cocos": "CCT-6:30", + "America/Boise": "MST7MDT,M3.2.0,M11.1.0", + "America/Guadeloupe": "AST4", + "America/Nassau": "EST5EDT,M3.2.0,M11.1.0", + "Europe/Prague": "CET-1CEST,M3.5.0,M10.5.0/3", + "Pacific/Enderbury": "PHOT-13", + "Asia/Hovd": "HOVT-7", + "America/Manaus": "AMT4", + "America/Godthab": "WGT3WGST,M3.5.0/-2,M10.5.0/-1", + "America/North_Dakota/Beulah": "CST6CDT,M3.2.0,M11.1.0", + "America/Chihuahua": "MST7MDT,M4.1.0,M10.5.0", + "America/Iqaluit": "EST5EDT,M3.2.0,M11.1.0", + "America/Argentina/Rio_Gallegos": "ART3", + "Pacific/Gambier": "GAMT9", + "Europe/Volgograd": "VOLT-4", + "Africa/Bamako": "GMT0", + "Asia/Novokuznetsk": "NOVT-7", + "Europe/Uzhgorod": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Africa/Banjul": "GMT0", + "Asia/Aqtau": "AQTT-5", + "Africa/Malabo": "WAT-1", + "Atlantic/Madeira": "WET0WEST,M3.5.0/1,M10.5.0", + "Pacific/Noumea": "NCT-11", + "Africa/Kinshasa": "WAT-1", + "Europe/Malta": "CET-1CEST,M3.5.0,M10.5.0/3", + "America/Argentina/Ushuaia": "ART3", + "Asia/Bangkok": "ICT-7", + "Pacific/Niue": "NUT11", + "Australia/Brisbane": "EST-10", + "America/Recife": "BRT3", + "Asia/Yerevan": "AMT-4", + "America/La_Paz": "BOT4", + "Africa/Cairo": "EET-2EEST,M4.5.5/0,M9.5.4/24", + "Africa/Lusaka": "CAT-2", + "Pacific/Guadalcanal": "SBT-11", + "America/Yellowknife": "MST7MDT,M3.2.0,M11.1.0", + "Asia/Vientiane": "ICT-7", + "Europe/Kaliningrad": "FET-3", + "Africa/Conakry": "GMT0", + "America/Argentina/Tucuman": "ART3", + "Asia/Hebron": "EET-2EEST,M3.5.4/24,M9.3.6/144", + "Europe/Oslo": "CET-1CEST,M3.5.0,M10.5.0/3", + "America/St_Kitts": "AST4", + "America/Panama": "EST5", + "Africa/Gaborone": "CAT-2", + "Pacific/Palau": "PWT-9", + "America/Guayaquil": "ECT5", + "Asia/Kuala_Lumpur": "MYT-8", + "America/Menominee": "CST6CDT,M3.2.0,M11.1.0", + "Asia/Kamchatka": "PETT-12", + "Asia/Vladivostok": "VLAT-11", + "America/Matamoros": "CST6CDT,M3.2.0,M11.1.0", + "Asia/Qatar": "AST-3", + "Asia/Dubai": "GST-4", + "Asia/Yakutsk": "YAKT-10", + "Asia/Omsk": "OMST-7", + "Africa/Bangui": "WAT-1", + "America/Paramaribo": "SRT3", + "Africa/Lubumbashi": "CAT-2", + "Pacific/Marquesas": "MART9:30", + "Europe/Bratislava": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Anadyr": "ANAT-12", + "America/New_York": "EST5EDT,M3.2.0,M11.1.0", + "Pacific/Norfolk": "NFT-11:30", + "Pacific/Rarotonga": "CKT10", + "America/Dominica": "AST4", + "Africa/Porto-Novo": "WAT-1", + "Asia/Samarkand": "UZT-5", + "Asia/Dushanbe": "TJT-5", + "America/Kentucky/Louisville": "EST5EDT,M3.2.0,M11.1.0", + "America/Toronto": "EST5EDT,M3.2.0,M11.1.0", + "America/Bahia": "BRT3", + "Africa/Kampala": "EAT-3", + "Africa/Ouagadougou": "GMT0", + "Asia/Muscat": "GST-4", + "America/Port_of_Spain": "AST4", + "Pacific/Wake": "WAKT-12", + "America/Indiana/Tell_City": "CST6CDT,M3.2.0,M11.1.0", + "Australia/Darwin": "CST-9:30", + "America/Whitehorse": "PST8PDT,M3.2.0,M11.1.0", + "America/Swift_Current": "CST6", + "Europe/Copenhagen": "CET-1CEST,M3.5.0,M10.5.0/3", + "America/Argentina/Salta": "ART3", + "America/Montserrat": "AST4", + "Europe/Simferopol": "MSK-4", + "Africa/Blantyre": "CAT-2", + "America/Detroit": "EST5EDT,M3.2.0,M11.1.0", + "America/Grenada": "AST4", + "America/Indiana/Vincennes": "EST5EDT,M3.2.0,M11.1.0", + "America/Indiana/Petersburg": "EST5EDT,M3.2.0,M11.1.0", + "Asia/Kathmandu": "NPT-5:45", + "Asia/Pontianak": "WIB-7", + "Africa/Dar_es_Salaam": "EAT-3", + "America/Port-au-Prince": "EST5EDT,M3.2.0,M11.1.0", + "America/Cayman": "EST5", + "Europe/Athens": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "America/Curacao": "AST4", + "Indian/Kerguelen": "TFT-5", + "Africa/Khartoum": "EAT-3", + "Asia/Manila": "PHT-8", + "Europe/Sarajevo": "CET-1CEST,M3.5.0,M10.5.0/3", + "Africa/Douala": "WAT-1", + "Europe/Rome": "CET-1CEST,M3.5.0,M10.5.0/3", + "America/Argentina/San_Juan": "ART3", + "America/North_Dakota/New_Salem": "CST6CDT,M3.2.0,M11.1.0", + "America/Kralendijk": "AST4", + "Pacific/Port_Moresby": "PGT-10", + "Europe/Jersey": "GMT0BST,M3.5.0/1,M10.5.0", + "Europe/Andorra": "CET-1CEST,M3.5.0,M10.5.0/3", + "Europe/Luxembourg": "CET-1CEST,M3.5.0,M10.5.0/3", + "Pacific/Honolulu": "HST10", + "America/St_Thomas": "AST4", + "Pacific/Majuro": "MHT-12", + "America/Mazatlan": "MST7MDT,M4.1.0,M10.5.0", + "Asia/Macau": "CST-8", + "Europe/Belgrade": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Choibalsan": "CHOT-8", + "Europe/Mariehamn": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Antarctica/McMurdo": "NZST-12NZDT,M9.5.0,M4.1.0/3", + "America/Thunder_Bay": "EST5EDT,M3.2.0,M11.1.0", + "America/Los_Angeles": "PST8PDT,M3.2.0,M11.1.0", + "Asia/Kabul": "AFT-4:30", + "Indian/Antananarivo": "EAT-3", + "Atlantic/Reykjavik": "GMT0", + "Asia/Nicosia": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "Pacific/Tongatapu": "TOT-13", + "America/Marigot": "AST4", + "Pacific/Pitcairn": "PST8", + "Pacific/Easter": "EAST6EASST,M9.1.6/22,M4.4.6/22", + "Atlantic/South_Georgia": "GST2", + "Africa/El_Aaiun": "WET0WEST,M3.5.0,M10.5.0/3", + "America/Campo_Grande": "AMT4AMST,M10.3.0/0,M2.3.0/0", + "America/Dawson_Creek": "MST7", + "Antarctica/Vostok": "VOST-6", + "Europe/Bucharest": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "America/Porto_Velho": "AMT4", + "Europe/Monaco": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Bishkek": "KGT-6", + "Africa/Ceuta": "CET-1CEST,M3.5.0,M10.5.0/3", + "America/Winnipeg": "CST6CDT,M3.2.0,M11.1.0", + "Asia/Aqtobe": "AQTT-5", + "Africa/Dakar": "GMT0", + "America/Fortaleza": "BRT3", + "Pacific/Tarawa": "GILT-12", + "America/Dawson": "PST8PDT,M3.2.0,M11.1.0", + "Africa/Addis_Ababa": "EAT-3", + "Pacific/Efate": "VUT-11", + "Pacific/Johnston": "HST10", + "Asia/Qyzylorda": "QYZT-6", + "Europe/San_Marino": "CET-1CEST,M3.5.0,M10.5.0/3", + "Asia/Jerusalem": "IST-2IDT,M3.4.4/26,M10.5.0", + "Pacific/Auckland": "NZST-12NZDT,M9.5.0,M4.1.0/3", + "America/Metlakatla": "MeST8", + "America/Tortola": "AST4", + "America/Denver": "MST7MDT,M3.2.0,M11.1.0", + "Indian/Chagos": "IOT-6", + "America/Glace_Bay": "AST4ADT,M3.2.0,M11.1.0", + "America/Hermosillo": "MST7", + "Africa/Tunis": "CET-1", + "America/Montevideo": "UYT3UYST,M10.1.0,M3.2.0", + "Asia/Ust-Nera": "VLAT-11", + "America/Resolute": "CST6CDT,M3.2.0,M11.1.0", + "Asia/Gaza": "EET-2EEST,M3.5.4/24,M9.3.6/144", + "Asia/Taipei": "CST-8", + "Antarctica/DumontDUrville": "DDUT-10", + "America/Argentina/Catamarca": "ART3", + "Antarctica/Troll": "UTC0CEST-2,M3.5.0/1,M10.5.0/3", + "Asia/Novosibirsk": "NOVT-7", + "Africa/Kigali": "CAT-2", + "America/Grand_Turk": "EST5EDT,M3.2.0,M11.1.0", + "Africa/Lagos": "WAT-1", + "Europe/Sofia": "EET-2EEST,M3.5.0/3,M10.5.0/4", + "America/Lima": "PET5", + "America/Anchorage": "AKST9AKDT,M3.2.0,M11.1.0", + "Pacific/Nauru": "NRT-12", +} From d919aba69d187188c5f7f809a56706e8318a7c77 Mon Sep 17 00:00:00 2001 From: Cooper Quintin Date: Tue, 12 Aug 2014 10:32:25 -0700 Subject: [PATCH 02/54] look up posix timezone in dictionary from olson tz --- routerapi/set_timezone | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/routerapi/set_timezone b/routerapi/set_timezone index 866cf59..5c759a2 100755 --- a/routerapi/set_timezone +++ b/routerapi/set_timezone @@ -4,22 +4,23 @@ import os import sys import common +from tz_info import tz_info TZ_PATH = "/etc/TZ" def jsonrpc_set_timezone(tz_path=TZ_PATH): - """Accept a POSIX-style set timezone, with parameters like so: + """Accept an olson-style set timezone, with parameters like so: {"jsonrpc":"2.0","method":"set_timezone","params":["timezonestring"],"id":1} - Set the local timezone on the router by placing the POSIX compatible timezone string + Set the local timezone on the router by looking up and placing the POSIX compatible timezone string in the /etc/TZ file. """ data = json.loads(sys.stdin.read()) try: params = data["params"] - tz_string = params[0] + tz_string = tz_info[params[0]] except KeyError, e: common.render_error(e.__str__()) except IndexError, e: From ff4629cef8247df7cffc69175bbb856265efa270 Mon Sep 17 00:00:00 2001 From: Cooper Quintin Date: Tue, 12 Aug 2014 10:32:49 -0700 Subject: [PATCH 03/54] include js library to get olson timezone --- app/changePassword.html | 1 + app/lib/jstz-1.0.4.min.js | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 app/lib/jstz-1.0.4.min.js diff --git a/app/changePassword.html b/app/changePassword.html index 750b0af..e0a6503 100644 --- a/app/changePassword.html +++ b/app/changePassword.html @@ -5,6 +5,7 @@ + diff --git a/app/lib/jstz-1.0.4.min.js b/app/lib/jstz-1.0.4.min.js new file mode 100644 index 0000000..96e3dd8 --- /dev/null +++ b/app/lib/jstz-1.0.4.min.js @@ -0,0 +1,2 @@ +/*! jstz - v1.0.4 - 2012-12-12 */ +(function(e){var t=function(){"use strict";var e="s",n=function(e){var t=-e.getTimezoneOffset();return t!==null?t:0},r=function(e,t,n){var r=new Date;return e!==undefined&&r.setFullYear(e),r.setDate(n),r.setMonth(t),r},i=function(e){return n(r(e,0,2))},s=function(e){return n(r(e,5,2))},o=function(e){var t=e.getMonth()>7?s(e.getFullYear()):i(e.getFullYear()),r=n(e);return t-r!==0},u=function(){var t=i(),n=s(),r=i()-s();return r<0?t+",1":r>0?n+",1,"+e:t+",0"},a=function(){var e=u();return new t.TimeZone(t.olson.timezones[e])};return{determine:a,date_is_dst:o}}();t.TimeZone=function(e){"use strict";var n=null,r=function(){return n},i=function(){var e=t.olson.ambiguity_list[n],r=e.length,i=0,s=e[0];for(;i Date: Tue, 12 Aug 2014 10:37:07 -0700 Subject: [PATCH 04/54] send olson timezone to python api --- app/js/changePassword.js | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/app/js/changePassword.js b/app/js/changePassword.js index a46ff66..f0b8a2d 100644 --- a/app/js/changePassword.js +++ b/app/js/changePassword.js @@ -61,20 +61,7 @@ var changePassword = (function() { */ var setTimeZone = function(response) { - - /** - * returns a POSIX compatible timezone string - * since we can't get the real timezone we return - * in the format off EFF\{offset\}local - */ - var getTzString = function() { - var d = new Date(); - var tzo = 1 + (d.getTimezoneOffset() / 60) - var tzstr = "EFF" + tzo + "local\n" - return tzstr; - }; - - var setTzRequest = { "jsonrpc": "2.0", "method": "set_timezone", "params": [getTzString()], "id": 1 }; + var setTzRequest = { "jsonrpc": "2.0", "method": "set_timezone", "params": [jstz.determine().name()], "id": 1 }; var request = { 'data': setTzRequest, 'url': setTzUrl, From 65009f28478beacf9efa6a38e9ec65ab56d06263 Mon Sep 17 00:00:00 2001 From: Cooper Quintin Date: Tue, 12 Aug 2014 10:45:20 -0700 Subject: [PATCH 05/54] updating test --- test/set_timezone_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/set_timezone_test.py b/test/set_timezone_test.py index 4a44d40..775c2b0 100644 --- a/test/set_timezone_test.py +++ b/test/set_timezone_test.py @@ -28,7 +28,7 @@ def tearDown(self): sys.stdin = self.saved_stdin def test_set_timezone(self): - setInput('{"jsonrpc":"2.0","method":"set_timezone","params":["EFF0local"],"id":1}') + setInput('{"jsonrpc":"2.0","method":"set_timezone","params":["America/Los_Angeles"],"id":1}') with self.assertRaises(SystemExit): jsonrpc_set_timezone(TZ_PATH) @@ -36,7 +36,7 @@ def test_set_timezone(self): with open(TZ_PATH, "r") as tz_file: tz_data = tz_file.read() - self.assertEqual(tz_data, "EFF0local") + self.assertEqual(tz_data, "PST8PDT,M3.2.0,M11.1.0") if __name__ == '__main__': unittest.main() From 5061544abc81f3a03a0ad9501faca1b2ed770434 Mon Sep 17 00:00:00 2001 From: Cooper Quintin Date: Tue, 12 Aug 2014 10:47:56 -0700 Subject: [PATCH 06/54] rename to snake case --- routerapi/{genTzDictionary.py => gen_tz_dictionary.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename routerapi/{genTzDictionary.py => gen_tz_dictionary.py} (93%) diff --git a/routerapi/genTzDictionary.py b/routerapi/gen_tz_dictionary.py similarity index 93% rename from routerapi/genTzDictionary.py rename to routerapi/gen_tz_dictionary.py index 50c6af9..a56e59e 100644 --- a/routerapi/genTzDictionary.py +++ b/routerapi/gen_tz_dictionary.py @@ -1,6 +1,6 @@ #!/usr/bin/python """ -genTzDictionary - Generate a python file containing a dictionary that maps +gen_tz_dictionary - Generate a python file containing a dictionary that maps Olson Time Zones to their POSIX equivalents. """ @@ -68,7 +68,7 @@ def write_zone_file(tz_dict): """ with open(PY_TZ_FILE_PATH, 'w') as py_file: header = ("#!/usr/bin/python\n" - "\"\"\"\ntz_info - maps Olson time zones to POSIX equivalents\nAutogenerated by genTzDictionary.py\n\"\"\"\n" + "\"\"\"\ntz_info - maps Olson time zones to POSIX equivalents\nAutogenerated by gen_tz_dictionary.py\n\"\"\"\n" "tz_info = {\n") footer = "}\n" From d78e43cb32cdeeb062b38d01400e19de667c026c Mon Sep 17 00:00:00 2001 From: Cooper Quintin Date: Tue, 12 Aug 2014 11:05:07 -0700 Subject: [PATCH 07/54] tests for gen_tz_dictionary --- routerapi/gen_tz_dictionary.py | 6 +++--- test/gen_tz_dictionary_test.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 test/gen_tz_dictionary_test.py diff --git a/routerapi/gen_tz_dictionary.py b/routerapi/gen_tz_dictionary.py index a56e59e..7cacd82 100644 --- a/routerapi/gen_tz_dictionary.py +++ b/routerapi/gen_tz_dictionary.py @@ -22,14 +22,14 @@ def generate_dictionary_file(): exit(1) -def get_zone_info(): +def get_zone_info(zone_dir=TZ_DB): """ get_zone_info - parse zone info from zoneinfo database into a python dictionary """ tzd = {} try: - with open(os.path.join(TZ_DB, "zone.tab"), "r") as zonetab: + with open(os.path.join(zone_dir, "zone.tab"), "r") as zonetab: for line in zonetab: #skip comments and whitespace if re.match("^#", line) or re.match("^\s+$", line): @@ -47,7 +47,7 @@ def get_zone_info(): return tzd except EnvironmentError: - return false + return False def read_posix_zone(zone): diff --git a/test/gen_tz_dictionary_test.py b/test/gen_tz_dictionary_test.py new file mode 100644 index 0000000..4bc7193 --- /dev/null +++ b/test/gen_tz_dictionary_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python2.7 + +import unittest, sys, os + +sys.path.insert(0, os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", "routerapi")) + +import gen_tz_dictionary + +class genTzDictionaryTest(unittest.TestCase): + + def test_get_zone_info(self): + tzd = gen_tz_dictionary.get_zone_info() + self.assertTrue(type(tzd) is dict) + self.assertTrue(len(tzd) > 1) + self.assertEqual(tzd["America/Los_Angeles"], "PST8PDT,M3.2.0,M11.1.0") + + self.assertFalse(gen_tz_dictionary.get_zone_info("/does/not/exist")) + + + def test_read_posix_zone(self): + posix_zone = gen_tz_dictionary.read_posix_zone("America/Los_Angeles") + self.assertEqual(posix_zone, "PST8PDT,M3.2.0,M11.1.0") + + with self.assertRaises(EnvironmentError): + gen_tz_dictionary.read_posix_zone("Fake/Zone") + + +if __name__ == '__main__': + unittest.main() + From 9ae5099779111c0318e275c952cd563b8de86aab Mon Sep 17 00:00:00 2001 From: Cooper Quintin Date: Wed, 13 Aug 2014 15:57:46 -0700 Subject: [PATCH 08/54] style nits --- routerapi/gen_tz_dictionary.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/routerapi/gen_tz_dictionary.py b/routerapi/gen_tz_dictionary.py index 7cacd82..859c157 100644 --- a/routerapi/gen_tz_dictionary.py +++ b/routerapi/gen_tz_dictionary.py @@ -18,7 +18,7 @@ def generate_dictionary_file(): write_zone_file(get_zone_info()) exit(0) - #exit with error status if we couldn't read zone info + # Exit with error status if we couldn't read zone info exit(1) @@ -31,22 +31,21 @@ def get_zone_info(zone_dir=TZ_DB): try: with open(os.path.join(zone_dir, "zone.tab"), "r") as zonetab: for line in zonetab: - #skip comments and whitespace + # Skip comments and whitespace if re.match("^#", line) or re.match("^\s+$", line): continue - #olson zone paths are the third column in the table - sep = re.compile("\s+") - zone = sep.split(line)[2] + # Olson zone paths are the third column in the table + zone = re.split("\s+", line)[2] try: tzd[zone] = read_posix_zone(zone) - except EnvironmentError: #IOError or OSError + except EnvironmentError: # IOError or OSError continue return tzd - except EnvironmentError: + except EnvironmentError: # IOError or OSError return False @@ -56,7 +55,7 @@ def read_posix_zone(zone): @param string zone - olson time zone string """ with open(os.path.join(TZ_DB, zone), "r") as zonefile: - #return last line of file + # Return last line of file return list(zonefile)[-1].rstrip() From 2a0ad20d6057a7a2455b167a47f8d5cb7a660374 Mon Sep 17 00:00:00 2001 From: Scott Arciszewski Date: Sat, 4 Oct 2014 17:20:38 -0400 Subject: [PATCH 09/54] Update diceware.js Prevent whitespace bugs --- app/js/diceware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/js/diceware.js b/app/js/diceware.js index cd3be2d..5bd6257 100644 --- a/app/js/diceware.js +++ b/app/js/diceware.js @@ -25,7 +25,7 @@ Diceware.prototype.load = function(callback) { if (line === '-----BEGIN PGP SIGNATURE-----') { break; } - var myregexp = /^\d+\s*([^\s]+)$/; + var myregexp = /^\s*?\d+\s*([^\s]+)\s*?$/; var match = myregexp.exec(line); if (match != null) { // matched text: match[0] From 8075811b59bab6e4f4c79879e4787b24cdbb260d Mon Sep 17 00:00:00 2001 From: Scott Arciszewski Date: Mon, 6 Oct 2014 11:20:57 -0400 Subject: [PATCH 10/54] Update diceware.js --- app/js/diceware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/js/diceware.js b/app/js/diceware.js index 5bd6257..2cea62a 100644 --- a/app/js/diceware.js +++ b/app/js/diceware.js @@ -25,7 +25,7 @@ Diceware.prototype.load = function(callback) { if (line === '-----BEGIN PGP SIGNATURE-----') { break; } - var myregexp = /^\s*?\d+\s*([^\s]+)\s*?$/; + var myregexp = /^\s*\d+\s*([^\s]+)\s*$/; var match = myregexp.exec(line); if (match != null) { // matched text: match[0] From 70970de1a14d05edf0537b0be504783b95af41a7 Mon Sep 17 00:00:00 2001 From: jsha Date: Wed, 29 Oct 2014 17:29:09 -0700 Subject: [PATCH 11/54] Update README.md --- README.md | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/README.md b/README.md index 2a07e9b..697a506 100644 --- a/README.md +++ b/README.md @@ -8,34 +8,6 @@ OpenWireless router firmware, which is based off of Cerowrt and OpenWRT. More details about the OpenWireless project can be found at https://openwireless.org/. -# Quick Look - -If you'd just like to take a quick look at the web UI, we have set up -several instances of the web UI to be publicly accessible. Please try out one of -these instances and report to us any vulnerabilities you find. Feel free to set -an admin password: These instances will reset at the top of each hour. - -[0](http://ow.crud.net:8000) -[1](http://ow.crud.net:8001) -[2](http://ow.crud.net:8002) -[3](http://ow.crud.net:8003) -[4](http://ow.crud.net:8004) -[5](http://ow.crud.net:8005) -[6](http://ow.crud.net:8006) -[7](http://ow.crud.net:8007) -[8](http://ow.crud.net:8008) -[9](http://ow.crud.net:8009) -[10](http://ow.crud.net:8010) -[11](http://ow.crud.net:8011) -[12](http://ow.crud.net:8012) -[13](http://ow.crud.net:8013) -[14](http://ow.crud.net:8014) -[15](http://ow.crud.net:8015) -[16](http://ow.crud.net:8016) -[17](http://ow.crud.net:8017) -[18](http://ow.crud.net:8018) -[19](http://ow.crud.net:8019) - # Getting Started Get the packages you need and install a git hook to run tests before push: From 0a9e71e8f0c0503585a80b543ea64fe9edf47902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c0ff3m4kr=20=E3=8B=9B?= Date: Tue, 4 Nov 2014 11:01:27 +0100 Subject: [PATCH 12/54] Wildcard for HANDLEBARS_FILES Using wildcard command to get all the .handlebar files. TEMPLATES_JS will be generated again when the HANDLEBARS_FILES are newer. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9c9ac44..5ce77e0 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TEMPLATES_JS=app/js/templates.js -HANDLEBARS_FILES=app/templates/*.handlebars # Used to generate templates.js +HANDLEBARS_FILES=$(wildcard app/templates/*.handlebars) # Used to generate templates.js .PHONY: all all: $(TEMPLATES_JS) From daabc5536f54ad5034ab15644d25b17c4589c875 Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Wed, 5 Nov 2014 15:05:50 +0100 Subject: [PATCH 13/54] Catch exceptions --- routerapi/check_updates | 3 +++ 1 file changed, 3 insertions(+) diff --git a/routerapi/check_updates b/routerapi/check_updates index 44eba55..eb73f64 100755 --- a/routerapi/check_updates +++ b/routerapi/check_updates @@ -14,6 +14,9 @@ try: ret = "not-up-to-date" else: ret = "up-to-date" +except: + # catch exception and return error message + pass finally: nullfile.close() From 70313087b35ffb9bd96d71cdcad818c7a2c8554e Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Thu, 6 Nov 2014 12:10:20 +0100 Subject: [PATCH 14/54] only catch OSError Also using the same code for update.py --- routerapi/check_updates | 4 ++-- routerapi/update | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/routerapi/check_updates b/routerapi/check_updates index eb73f64..ab8bd56 100755 --- a/routerapi/check_updates +++ b/routerapi/check_updates @@ -14,8 +14,8 @@ try: ret = "not-up-to-date" else: ret = "up-to-date" -except: - # catch exception and return error message +except OSError: + # catch call-errors and return error message pass finally: nullfile.close() diff --git a/routerapi/update b/routerapi/update index 5e0827e..ef9f4de 100755 --- a/routerapi/update +++ b/routerapi/update @@ -14,6 +14,9 @@ try: ret = "update-success" # Should never reach this line of code else: ret = "update-failure" +except OSError: + # catch call-errors and return error message + pass finally: nullfile.close() From c592d159653ddf9ada287233ab9da3985eec82b3 Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Thu, 6 Nov 2014 14:45:53 +0100 Subject: [PATCH 15/54] Code style improvement Packed main code into methods Moved globals to attribute functions Added exception which is raised instead of calling from inside the class. Added some docstrings. Replaced has_key with . --- lib/update/update.py | 163 +++++++++++++++++++++++++++---------------- 1 file changed, 104 insertions(+), 59 deletions(-) diff --git a/lib/update/update.py b/lib/update/update.py index d3244f6..9457f09 100755 --- a/lib/update/update.py +++ b/lib/update/update.py @@ -4,40 +4,54 @@ import systemwide_lock import time -openwrt_release_file = "/etc/openwrt_release" -keyring = "/etc/update_key.gpg" -update_url = "https://s.eff.org/files/openwireless/update.json.asc" -sysupgrade_command = ["/usr/bin/sudo", "sysupgrade", "-v", "-n"] -update_check_file = "/etc/last_update_check" def failed(why): sys.stderr.write("Failed %s\n" % why) systemwide_lock.release_lock() sys.exit(1) +class UpdateError(Exception): + '''Exception raised if an error occures while updating. + ''' + def __init__(self, message): + self.message = message + + def __str__(self): + return ("Failed " + self.message) + class Updater(object): + + openwrt_release_file = "/etc/openwrt_release" + keyring = "/etc/update_key.gpg" + update_url = "https://s.eff.org/files/openwireless/update.json.asc" + sysupgrade_command = ["/usr/bin/sudo", "sysupgrade", "-v", "-n"] + update_check_file = "/etc/last_update_check" + def __init__(self): self.firmware = None self.current = None - with open(openwrt_release_file) as f: + with open(self.openwrt_release_file) as f: for line in f: m = re.match(r'^DISTRIB_RELEASE_DATE="(\d+)"$', line) if m and len(m.groups()) == 1: self.current = int(m.groups()[0]) break - else: - failed("to find current version in /etc/openwrt_release") + if self.current is None: + raise UpdateError("to find current version in " + self.openwrt_release_file) self.purported_manifest = None self.manifest = None def get_manifest(self): + '''Download the manifest from eff.org. + Returns True on success and False if the download failed. + ''' buffer = StringIO.StringIO() curl = pycurl.Curl() curl.setopt(pycurl.PROXYPORT, 9050) curl.setopt(pycurl.PROXY, "localhost") curl.setopt(pycurl.PROXYTYPE, 6) # == PROXYTYPE_SOCKS4A curl.setopt(pycurl.CAINFO, "/etc/ssl/certs/StartCom_Certification_Authority.crt") - curl.setopt(pycurl.URL, update_url) + curl.setopt(pycurl.URL, self.update_url) curl.setopt(pycurl.WRITEFUNCTION, buffer.write) try: curl.perform() @@ -53,7 +67,8 @@ def valid_sig(self): if not self.purported_manifest: return False in_fd, out_fd = os.pipe() - command = ["gpg", "--keyring", keyring, "--no-default-keyring", "--status-fd", str(out_fd), "--decrypt"] + command = ["gpg", "--keyring", self.keyring, "--no-default-keyring", + "--status-fd", str(out_fd), "--decrypt"] p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc_stdout, proc_stderr = p.communicate(self.purported_manifest) if p.returncode != 0: @@ -73,6 +88,9 @@ def valid_sig(self): return True def download_file(self): + '''Downloads the update image to a temporary file and saves its + location to the attribute `firmware`. + ''' if not self.manifest or not self.manifest.has_key("url"): return None url = self.manifest["url"] @@ -92,7 +110,9 @@ def download_file(self): return True def valid_firmware(self): - if not (self.manifest.has_key("sha256") and self.manifest.has_key("size") and self.manifest.has_key("timestamp")): + if not self.manifest: + return False + if not ("sha256" in self.manifest and "size" in self.manifest and "timestamp" in self.manifest): return False if not self.is_newer(): return False @@ -116,28 +136,83 @@ def do_update(self): # This is unlikely to actually return if successful, because the # sysupgrade command itself will likely kill the upgrader process # and also reboot the device. - return not subprocess.call(sysupgrade_command + [self.firmware]) + return not subprocess.call(self.sysupgrade_command + [self.firmware]) def extract_manifest(self, what): try: extracted_version = json.loads(what) if not isinstance(extracted_version, dict): - failed("to extract a JSON-structured update manifest") + raise UpdateError("to extract a JSON-structured update manifest") else: return extracted_version except ValueError, e: - failed("to extract a JSON-structured update manifest") - failed("not reached") + raise UpdateError("to extract a JSON-structured update manifest") + raise UpdateError("not reached") def parse_manifest(self): if not self.purported_manifest: - raise Exception, "cannot get manifest" + raise UpdateError("to get the manifest") if self.valid_sig(): self.manifest = self.extract_manifest(self.signed_manifest) return True else: return False + @staticmethod + def __update_check_file(newer): + """Store the current time (in Javascript format) in the file that + tracks when we last checked for updates. + """ + msg = "Updating " + self.update_check_file + " to indicate " + if not newer: + flag = " N" + msg += "no installable" + else: + flag = " Y" + msg += "an" + msg += " update is available." + with open(self.update_check_file, "w") as checkfile: + checkfile.write(repr(time.time()*1000) + flag) + return True + + def check_for_updates(self, advise_only=False): + """Checks for new software version. If advise_only is True it doesn't + call do_update() otherwise True is returned if a new firmware version + is available and False if it is the current. + """ + if not self.purported_manifest: + print "Getting update metadata..." + if not u.get_manifest(): + raise UpdateError("to download update metadata") + print u.purported_manifest + + if not self.manifest: + print "Validating update signature..." + if not self.parse_manifest(): + # Parsing includes signature validity checking + raise UpdateError("to validate signature of update metdata") + + print "Checking whether to update..." + newer = self.is_newer() + self.__update_check_file(newer) + if advise_only: + # In this case, the update script is being run merely to check + # and inform the user whether an update is available, not to + # install it. + return newer + + print "Downloading new firmware image..." + if not self.download_file(): + raise UpdateError("to download firmware image") + + print "Validating downloaded image..." + if not self.valid_firmware(): + raise UpdateError("to validate downloaded firmware image") + + print "Installing image..." + return self.do_update() + + if __name__ == '__main__': advise_only = False if len(sys.argv) > 1 and sys.argv[1] == 'check': @@ -147,52 +222,22 @@ def parse_manifest(self): if not systemwide_lock.get_lock(): failed("to acquire update lock") u = Updater() - print "Getting update metadata..." - if not u.get_manifest(): - failed("to download update metadata") - print u.purported_manifest - print "Validating update signature..." - if not u.parse_manifest(): # includes signature validity checking - failed("to validate signature of update metdata") - - print "Checking whether to update..." + result = u.check_for_updates(advise_only) if advise_only: - # In this case, the update script is being run merely to check - # and inform the user whether an update is available, not to - # install it. - # Store the current time (in Javascript format) in the - # file that tracks when we last checked for updates. - if u.is_newer(): - print "Updating " + update_check_file + " to indicate update is available." - with open(update_check_file, "w") as f: - f.write(repr(time.time()*1000) + " Y") - systemwide_lock.release_lock() + # Release lock is called in the finally block + if result: + # update is available sys.exit(0) - else: - print "Updating " + update_check_file + " to indicate no installable update available." - with open(update_check_file, "w") as f: - f.write(repr(time.time()*1000) + " N") - systemwide_lock.release_lock() - sys.exit(1) - if u.is_newer(): - print "Updating " + update_check_file + " to indicate update is available." - with open(update_check_file, "w") as f: - f.write(repr(time.time()*1000) + " Y") - print "Downloading new firmware image..." - if not u.download_file(): - failed("to download firmware image") - print "Validating downloaded image..." - if u.valid_firmware(): - print "Installing image..." - if not u.do_update(): - failed("to install update") - subprocess.call(["/usr/bin/sudo", "/sbin/reboot"]) - else: - failed("to validate downloaded firmware image") + # current version + sys.exit(1) else: - print "Updating " + update_check_file + " to indicate no installable update available." - with open(update_check_file, "w") as f: - f.write(repr(time.time()*1000) + " N") + if not result: + failed("to install update") + # Reboot if the installation completed but we're still alive + subprocess.call(["/usr/bin/sudo", "/sbin/reboot"]) + except UpdateError as why: + # Catch "trusted" exception + failed(why.message) except Exception, e: print e failed("to update for an undetermined reason.") From c9e16ed3d615d8bc0afd3f91818669799f51526a Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Thu, 6 Nov 2014 14:50:00 +0100 Subject: [PATCH 16/54] fixes typo --- lib/update/update.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/update/update.py b/lib/update/update.py index 9457f09..1532fbc 100755 --- a/lib/update/update.py +++ b/lib/update/update.py @@ -182,9 +182,9 @@ def check_for_updates(self, advise_only=False): """ if not self.purported_manifest: print "Getting update metadata..." - if not u.get_manifest(): + if not self.get_manifest(): raise UpdateError("to download update metadata") - print u.purported_manifest + print self.purported_manifest if not self.manifest: print "Validating update signature..." From e211f65872fb13b44e149b9983251db3edd020ad Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Thu, 6 Nov 2014 14:59:02 +0100 Subject: [PATCH 17/54] certificate as attribute --- lib/update/update.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/update/update.py b/lib/update/update.py index 1532fbc..29088ce 100755 --- a/lib/update/update.py +++ b/lib/update/update.py @@ -26,7 +26,8 @@ class Updater(object): update_url = "https://s.eff.org/files/openwireless/update.json.asc" sysupgrade_command = ["/usr/bin/sudo", "sysupgrade", "-v", "-n"] update_check_file = "/etc/last_update_check" - + ca_file = "/etc/ssl/certs/StartCom_Certification_Authority.crt" + def __init__(self): self.firmware = None self.current = None @@ -50,7 +51,7 @@ def get_manifest(self): curl.setopt(pycurl.PROXYPORT, 9050) curl.setopt(pycurl.PROXY, "localhost") curl.setopt(pycurl.PROXYTYPE, 6) # == PROXYTYPE_SOCKS4A - curl.setopt(pycurl.CAINFO, "/etc/ssl/certs/StartCom_Certification_Authority.crt") + curl.setopt(pycurl.CAINFO, self.ca_file) curl.setopt(pycurl.URL, self.update_url) curl.setopt(pycurl.WRITEFUNCTION, buffer.write) try: @@ -99,7 +100,7 @@ def download_file(self): curl = pycurl.Curl() curl.setopt(pycurl.URL, self.manifest["url"].encode("ascii")) curl.setopt(pycurl.WRITEFUNCTION, buffer.write) - curl.setopt(pycurl.CAINFO, "/etc/ssl/certs/StartCom_Certification_Authority.crt") + curl.setopt(pycurl.CAINFO, self.ca_file) try: curl.perform() except Exception, e: From 6e0b218b3965b680d6cb3721ae6b4a50be4c0ddc Mon Sep 17 00:00:00 2001 From: coff3m4kr Date: Tue, 18 Nov 2014 19:29:09 +0100 Subject: [PATCH 18/54] Require a password which is at least 8 characters long --- routerapi/auth.py | 6 ++++++ routerapi/change_password | 3 ++- routerapi/change_password_first_time | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/routerapi/auth.py b/routerapi/auth.py index 86269b6..a780323 100644 --- a/routerapi/auth.py +++ b/routerapi/auth.py @@ -80,6 +80,7 @@ class Auth: RATE_LIMIT_DURATION = 86400 # One day in seconds RATE_LIMIT_COUNT = 10 LOGGED_IN_COOKIE_NAME = 'logged_in' + PASSWORD_LENGTH_MIN = 8 def __init__(self, path = default_path()): self.path = path @@ -167,11 +168,16 @@ def is_password(self, candidate): def save_password(self, new_password): """ Store a new password. + Returns True is the password was stored and False if the password + didn't fulfil all criteria. """ + if len(new_password) < self.PASSWORD_LENGTH_MIN: + return False # 55 iterations takes about 100 ms on a Netgear WNDR3800 or about 8ms on a # Core2 Duo at 1200 MHz. hashed = pbkdf2.crypt(new_password, iterations=55) self.write(self.password_filename, hashed) + return True def get_csrf_token(self): """ diff --git a/routerapi/change_password b/routerapi/change_password index d4bf32b..a620445 100755 --- a/routerapi/change_password +++ b/routerapi/change_password @@ -29,7 +29,8 @@ def jsonrpc_change_password(): a = auth.Auth() if a.is_password(old_password): - a.save_password(new_password) + if not a.save_password(new_password): + common.render_error("Invalid password supplied.") print "Content-Type: application/json" print a.login_headers() print diff --git a/routerapi/change_password_first_time b/routerapi/change_password_first_time index 30f928a..06dfa11 100755 --- a/routerapi/change_password_first_time +++ b/routerapi/change_password_first_time @@ -33,7 +33,8 @@ def jsonrpc_change_password_first_time(auth_path): if a.password_exists(): common.render_error('Administrator password has already been set.') else: - a.save_password(new_password) + if not a.save_password(new_password): + common.render_error("Invalid password supplied.") uci.set('openwireless.setup_state', 'setup-private-net') uci.commit('openwireless') print "Content-Type: application/json" From 03058ff34457720fb97ed8d5a100a1c96fd0ab3b Mon Sep 17 00:00:00 2001 From: coff3m4kr Date: Tue, 18 Nov 2014 19:43:56 +0100 Subject: [PATCH 19/54] check private wlan password length --- routerapi/set_private_ssid | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routerapi/set_private_ssid b/routerapi/set_private_ssid index bbc29a1..a654e6b 100755 --- a/routerapi/set_private_ssid +++ b/routerapi/set_private_ssid @@ -16,7 +16,8 @@ def jsonrpc_set_private_ssid(): common.render_error(e.__str__()) except ValueError, e: common.render_error(e.__str__()) - + if len(passphrase) < 8: + common.render_error("Passphrase must contain at least 8 characters.") # TODO: filter input uci.set('wireless.@wifi-iface[2].ssid', name) uci.set('wireless.@wifi-iface[2].key', passphrase) From 4dab0a13cd55ace8261fa8f83ef602a4a822dafb Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Tue, 18 Nov 2014 10:58:22 -0800 Subject: [PATCH 20/54] Added basic vagrant file with more dependencies required for building. * Using vagrant greatly reduces the barrier to contribution as it allows folks with different operating systems to get up and running quickly. * Creates a known development environment which can be centrally maintained. * Reduce the getting started documentation. --- .gitignore | 1 + Vagrantfile | 27 +++++++++++++++++++++++++++ install-dev-dependencies.sh | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 Vagrantfile diff --git a/.gitignore b/.gitignore index 2ab3a11..5783c09 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /env/ local-lighttpd/etc local-lighttpd/port-*-etc +.vagrant diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..a61e591 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,27 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + config.vm.box = "ubuntu/trusty64" + + config.vm.provider "virtualbox" do |v| + v.memory = 4048 + v.cpus = 4 + end + + config.vm.provision "shell", inline: < Date: Fri, 21 Nov 2014 15:15:25 -0800 Subject: [PATCH 21/54] Only link pre-push hook if it does not already exist. * This is required to allow install-dev-dependencies.sh idempotent (able to be run multiple times). --- install-dev-dependencies.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install-dev-dependencies.sh b/install-dev-dependencies.sh index 560ed6d..5473b95 100755 --- a/install-dev-dependencies.sh +++ b/install-dev-dependencies.sh @@ -6,6 +6,8 @@ cd $(dirname $0) pip install --user -qr requirements.txt npm install # Install a hook to run tests before pushing. -ln -s ../../run-tests.sh .git/hooks/pre-push +if [ ! -f .git/hooks/pre-push ]; then + ln -s ../../run-tests.sh .git/hooks/pre-push +fi echo "We strongly recommend adding the lines from ./ssh-config to your" \ "SSH config. It will make developing on a router much faster." From 298e7846e3ff4ecf2770933875bf43fb7477f42a Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sat, 22 Nov 2014 02:04:01 -0800 Subject: [PATCH 22/54] Add tftp to install-dev-dependencies. * tftp is required in dev for flashing the wndr3800 --- install-dev-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install-dev-dependencies.sh b/install-dev-dependencies.sh index 5473b95..352f9ca 100755 --- a/install-dev-dependencies.sh +++ b/install-dev-dependencies.sh @@ -1,7 +1,7 @@ #!/bin/bash -ex # On Ubuntu and similar systems, install packages that are necessary and/or # useful to build and debug OpenWireless. -sudo apt-get install -y gettext unzip libncurses-dev subversion git inotify-tools lighttpd nodejs npm python2.7 python-pip +sudo apt-get install -y gettext unzip libncurses-dev subversion git inotify-tools lighttpd nodejs npm python2.7 python-pip tftp cd $(dirname $0) pip install --user -qr requirements.txt npm install From 588997176828833cc8d262376e9bd0b49bff198e Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sat, 22 Nov 2014 02:07:24 -0800 Subject: [PATCH 23/54] Build image w/ image builder. * Aim of this commit is to reduce build time in development. With this building firmware from clean is about 8min in virtual machine, rather than about 3hrs. * Downloads and caches ImageBuilder from cerowrt/bufferbloat repository. * Larger than a regular release. * Currently has vanilla python rather than python-mini-eff. Building python-mini-eff as a binary and hosting would resolve this issue. * Caches all downloaded packages in .cache --- .gitignore | 4 ++- infra/repositories.conf | 11 +++++++ scripts/build-with-image-builder | 50 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 infra/repositories.conf create mode 100755 scripts/build-with-image-builder diff --git a/.gitignore b/.gitignore index 5783c09..93cd2d3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ /env/ local-lighttpd/etc local-lighttpd/port-*-etc -.vagrant +/.vagrant +/.cache +/dist diff --git a/infra/repositories.conf b/infra/repositories.conf new file mode 100644 index 0000000..98d7d09 --- /dev/null +++ b/infra/repositories.conf @@ -0,0 +1,11 @@ +src/gz bufferbloat http://snapon.lab.bufferbloat.net/~cero2/cerowrt/wndr/3.10.50-1/packages +src/gz barrier_breaker_base http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/packages/base +src/gz barrier_breaker_luci http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/packages/luci +src/gz barrier_breaker_management http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/packages/management +src/gz barrier_breaker_oldpackages http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/packages/oldpackages +src/gz barrier_breaker_packages http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/packages/packages +src/gz barrier_breaker_routing http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/packages/routing +src/gz barrier_breaker_telephony http://downloads.openwrt.org/barrier_breaker/14.07/ar71xx/generic/packages/telephony + +## This is the local package repository, do not remove! +src imagebuilder file:packages diff --git a/scripts/build-with-image-builder b/scripts/build-with-image-builder new file mode 100755 index 0000000..f8e0e7a --- /dev/null +++ b/scripts/build-with-image-builder @@ -0,0 +1,50 @@ +#!/bin/sh + +set -e +set -x + +ROOT=$(cd `dirname $0`/.. && pwd) +CACHE=$ROOT/.cache +TARGET=`mktemp -d /tmp/openwireless-XXXX`/target +IMAGE_BUILDER=OpenWrt-ImageBuilder-ar71xx_generic-for-linux-x86_64 +IMAGE_BUILDER_URL=http://snapon.lab.bufferbloat.net/~cero2/cerowrt/wndr/3.10.50-1/$IMAGE_BUILDER.tar.bz2 +IMAGE_BUILDER_ARTIFACT=$IMAGE_BUILDER.tar.bz2 +REPOSITORIES_CONF=$ROOT/infra/repositories.conf +FILES=$TARGET/files + +rm -Rf $TARGET +mkdir -p $TARGET +mkdir -p $CACHE +if [ ! -f $CACHE/$IMAGE_BUILDER_ARTIFACT ]; then + curl -o $CACHE/$IMAGE_BUILDER_ARTIFACT $IMAGE_BUILDER_URL +fi +cp $CACHE/$IMAGE_BUILDER_ARTIFACT $TARGET +cd $TARGET +tar xf $IMAGE_BUILDER_ARTIFACT +cd $ROOT +cp $REPOSITORIES_CONF $TARGET/$IMAGE_BUILDER + +mkdir -p $FILES + +cp -rp $ROOT/etc $FILES/ + +cp -rp $ROOT/lib $FILES/ + +mkdir $FILES/www + +cp -rp $ROOT/app/* $FILES/www/ + +mkdir $FILES/www/cgi-bin + +cp -rp $ROOT/routerapi $FILES/www/cgi-bin/ + +cd $TARGET/$IMAGE_BUILDER + +rmdir dl +ln -s $CACHE dl + +make image PROFILE=WNDR3700 PACKAGES="dropbear lighttpd lighttpd-mod-alias lighttpd-mod-cgi lighttpd-mod-expire lighttpd-mod-redirect lighttpd-mod-setenv lighttpd-mod-simple-vhost python sudo sqm-scripts rsync 6in4 6rd 6to4 ahcpd avahi-daemon block-mount ca-certificates chat comgt curl dbus ddns-scripts dnsmasq-full dropbear-xinetd ds-lite e2fsprogs etherwake ethtool fping gnupg hd-idle hostapd hostapd-utils ip ip-full ip6tables-mod-nat ipset iptables-mod-account iptables-mod-extra iptables-mod-ipmark iptables-mod-u32 iptaccount kmod-8021q kmod-ath9k-htc kmod-atm kmod-crypto-authenc kmod-crypto-hash kmod-crypto-manager kmod-crypto-pcompress kmod-fs-autofs4 kmod-fs-ext4 kmod-fs-vfat kmod-gre kmod-ip6-tunnel kmod-ipip kmod-ipt-account kmod-ipt-compat-xtables kmod-ipt-extra kmod-ipt-hashlimit kmod-ipt-ipmark kmod-ipt-ipset kmod-ipt-nat6 kmod-ipt-u32 kmod-iptunnel kmod-iptunnel4 kmod-iptunnel6 kmod-leds-gpio kmod-ledtrig-default-on kmod-ledtrig-netdev kmod-ledtrig-timer kmod-lib-crc16 kmod-libphy kmod-mii kmod-nat46 kmod-nf-conntrack-netlink kmod-nfnetlink kmod-nls-cp437 kmod-nls-iso8859-1 kmod-nls-iso8859-13 kmod-nls-iso8859-15 kmod-nls-iso8859-2 kmod-pppoa kmod-pptp kmod-scsi-cdrom kmod-scsi-core kmod-sit kmod-swconfig kmod-usb-net kmod-usb-storage lft libavahi libavahi-dbus-support libblkid libcurl libdaemon libdbus libelf1 libevent2 libexpat libext2fs libgmp libgpg-error libiwinfo libmnl libmount libncurses libncursesw libnetfilter-conntrack libnettle libnfnetlink libpcap libpolarssl libpopt libreadline librt libuuid lighttpd-mod-fastcgi linux-atm miniupnpd mount-utils ntpclient ohybridproxy openssl-util opkg-smime ppp-mod-pppoa px5g python-curl relayd rng-tools rsync tcpdump-mini terminfo time tor wireless-tools wol xinetd zile" FILES=$FILES + +mkdir -p $ROOT/dist +rm -Rf $ROOT/dist/* +cp -R $TARGET/$IMAGE_BUILDER/bin/* $ROOT/dist From c61dcf8a743a711d67bed8a8f7559e15277f98a0 Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sat, 22 Nov 2014 11:59:35 -0800 Subject: [PATCH 24/54] Add libfontconfig to install-development-dependencies (required for phantomjs) --- install-dev-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install-dev-dependencies.sh b/install-dev-dependencies.sh index 352f9ca..2ce0fac 100755 --- a/install-dev-dependencies.sh +++ b/install-dev-dependencies.sh @@ -1,7 +1,7 @@ #!/bin/bash -ex # On Ubuntu and similar systems, install packages that are necessary and/or # useful to build and debug OpenWireless. -sudo apt-get install -y gettext unzip libncurses-dev subversion git inotify-tools lighttpd nodejs npm python2.7 python-pip tftp +sudo apt-get install -y gettext unzip libncurses-dev subversion git inotify-tools lighttpd nodejs npm python2.7 python-pip tftp libfontconfig cd $(dirname $0) pip install --user -qr requirements.txt npm install From 43d7ffa891039b9e4d93a8beb33246f0f37eb407 Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sat, 22 Nov 2014 11:59:56 -0800 Subject: [PATCH 25/54] Link legacy node binary to new binary. --- install-dev-dependencies.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install-dev-dependencies.sh b/install-dev-dependencies.sh index 2ce0fac..cc58fa0 100755 --- a/install-dev-dependencies.sh +++ b/install-dev-dependencies.sh @@ -2,6 +2,9 @@ # On Ubuntu and similar systems, install packages that are necessary and/or # useful to build and debug OpenWireless. sudo apt-get install -y gettext unzip libncurses-dev subversion git inotify-tools lighttpd nodejs npm python2.7 python-pip tftp libfontconfig +if [ ! -f /usr/bin/node ]; then + sudo ln -s /usr/bin/nodejs /usr/bin/node +fi cd $(dirname $0) pip install --user -qr requirements.txt npm install From 6e711fb8649b3c08727557f3dfadbe967c33a4bd Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sun, 23 Nov 2014 01:22:50 -0800 Subject: [PATCH 26/54] Add firefox/xvfb for running functional tests in virtual machine. * Also had to upgrade selenium version to support newer version of firefox. * Xvfb is a virtual framebuffer which allows running GUI applications without a real X windows environment. --- infra/etc/init.d/Xvfb | 34 ++++++++++++++++++++++++++++++++++ install-dev-dependencies.sh | 4 +++- requirements.txt | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100755 infra/etc/init.d/Xvfb diff --git a/infra/etc/init.d/Xvfb b/infra/etc/init.d/Xvfb new file mode 100755 index 0000000..60d09fe --- /dev/null +++ b/infra/etc/init.d/Xvfb @@ -0,0 +1,34 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: Xvfb +# Required-Start: $local_fs $remote_fs +# Required-Stop: +# X-Start-Before: +# Default-Start: 2 3 4 5 +# Default-Stop: +### END INIT INFO + +NAME=Xvfb +INIT=/etc/init.d/$NAME +PROGRAM=/usr/bin/$NAME +PIDFILE=/var/run/$NAME.pid +DISPLAY=:0 + +set -e + +case "$1" in + start) +start-stop-daemon --oknodo --background --make-pidfile --start --exec $PROGRAM --pidfile $PIDFILE -- $DIPLAY -screen 0 1024x768x24 +;; + stop) +start-stop-daemon --oknodo --stop --pidfile $PIDFILE +rm $PIDFILE +;; + *) +echo "Usage: $INIT {start|stop}" >&2 +exit 1 +;; +esac + +exit 0 diff --git a/install-dev-dependencies.sh b/install-dev-dependencies.sh index cc58fa0..d375bfb 100755 --- a/install-dev-dependencies.sh +++ b/install-dev-dependencies.sh @@ -1,7 +1,7 @@ #!/bin/bash -ex # On Ubuntu and similar systems, install packages that are necessary and/or # useful to build and debug OpenWireless. -sudo apt-get install -y gettext unzip libncurses-dev subversion git inotify-tools lighttpd nodejs npm python2.7 python-pip tftp libfontconfig +sudo apt-get install -y gettext unzip libncurses-dev subversion git inotify-tools lighttpd nodejs npm python2.7 python-pip tftp libfontconfig firefox xvfb if [ ! -f /usr/bin/node ]; then sudo ln -s /usr/bin/nodejs /usr/bin/node fi @@ -12,5 +12,7 @@ npm install if [ ! -f .git/hooks/pre-push ]; then ln -s ../../run-tests.sh .git/hooks/pre-push fi +sudo cp infra/etc/init.d/Xvfb /etc/init.d/Xvfb +sudo service Xvfb start echo "We strongly recommend adding the lines from ./ssh-config to your" \ "SSH config. It will make developing on a router much faster." diff --git a/requirements.txt b/requirements.txt index 876eda0..a86027e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ mock==1.0.1 -selenium==2.42.1 +selenium==2.44.0 From 8d1d496cd908394ddf87eb95726bb7c62383af2d Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sun, 23 Nov 2014 02:34:47 -0800 Subject: [PATCH 27/54] Refactor pre-push script to use wrapper 'pre-commit' * pre-commit should aim to do everything to validate developer changes, this may extend beyond just 'run-tests.sh' which runs unit tests. * pre-commit is vagrant aware, so will run the command on the vagrant box if vagrant is detected. * if already on the vagrant box, will run tests locally. --- install-dev-dependencies.sh | 2 +- scripts/pre-commit | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100755 scripts/pre-commit diff --git a/install-dev-dependencies.sh b/install-dev-dependencies.sh index d375bfb..124a336 100755 --- a/install-dev-dependencies.sh +++ b/install-dev-dependencies.sh @@ -10,7 +10,7 @@ pip install --user -qr requirements.txt npm install # Install a hook to run tests before pushing. if [ ! -f .git/hooks/pre-push ]; then - ln -s ../../run-tests.sh .git/hooks/pre-push + ln -s ../../scripts/pre-commit .git/hooks/pre-push fi sudo cp infra/etc/init.d/Xvfb /etc/init.d/Xvfb sudo service Xvfb start diff --git a/scripts/pre-commit b/scripts/pre-commit new file mode 100755 index 0000000..415d2a6 --- /dev/null +++ b/scripts/pre-commit @@ -0,0 +1,13 @@ +#!/bin/bash + +set -ex + +ROOT=`pwd` +USE_VAGRANT=`ls .vagrant &> /dev/null && test $(whoami) != "vagrant"; echo $?` +COMMAND="./run-tests.sh" + +if [ "$USE_VAGRANT" -eq "0" ]; then + vagrant ssh -c "cd /vagrant && $COMMAND" +else + $COMMAND +fi From 8f18b7f52b21432fbedfb2a2f0145a918a8b6ead Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sun, 23 Nov 2014 17:16:07 -0800 Subject: [PATCH 28/54] Fix DISPLAY setting in Xvfb init script. --- infra/etc/init.d/Xvfb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/infra/etc/init.d/Xvfb b/infra/etc/init.d/Xvfb index 60d09fe..08613f2 100755 --- a/infra/etc/init.d/Xvfb +++ b/infra/etc/init.d/Xvfb @@ -9,17 +9,18 @@ # Default-Stop: ### END INIT INFO +set -e +set -o nounset + NAME=Xvfb INIT=/etc/init.d/$NAME PROGRAM=/usr/bin/$NAME PIDFILE=/var/run/$NAME.pid DISPLAY=:0 -set -e - case "$1" in start) -start-stop-daemon --oknodo --background --make-pidfile --start --exec $PROGRAM --pidfile $PIDFILE -- $DIPLAY -screen 0 1024x768x24 +start-stop-daemon --oknodo --background --make-pidfile --start --exec $PROGRAM --pidfile $PIDFILE -- $DISPLAY -screen 0 1024x768x24 ;; stop) start-stop-daemon --oknodo --stop --pidfile $PIDFILE From ec0e457806dd6cff4f0a67e8928b4883e06d2d87 Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Mon, 24 Nov 2014 15:25:57 +0100 Subject: [PATCH 29/54] test case --- test/auth_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/auth_test.py b/test/auth_test.py index 6fd5dcc..e898305 100644 --- a/test/auth_test.py +++ b/test/auth_test.py @@ -58,6 +58,8 @@ def test_password(self): self.assertTrue(os.path.isfile(os.path.join(self.path, "password"))) self.assertTrue(self.auth.is_password("Passw0rd")) self.assertFalse(self.auth.is_password("badpass")) + self.assertFalse(self.auth.save_password("2Short")) + self.assertTrue(self.auth.is_password("Passw0rd")) def test_write(self): filename = os.path.join(self.path, "foo") From 6f70f64726b4b3b5edb470fbf8b7e26ef1285d28 Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Mon, 24 Nov 2014 15:32:38 +0100 Subject: [PATCH 30/54] Revert "test case" This reverts commit ec0e457806dd6cff4f0a67e8928b4883e06d2d87. --- test/auth_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/auth_test.py b/test/auth_test.py index e898305..6fd5dcc 100644 --- a/test/auth_test.py +++ b/test/auth_test.py @@ -58,8 +58,6 @@ def test_password(self): self.assertTrue(os.path.isfile(os.path.join(self.path, "password"))) self.assertTrue(self.auth.is_password("Passw0rd")) self.assertFalse(self.auth.is_password("badpass")) - self.assertFalse(self.auth.save_password("2Short")) - self.assertTrue(self.auth.is_password("Passw0rd")) def test_write(self): filename = os.path.join(self.path, "foo") From cb429cfda2bd265b2d2820cf48fbc0ca4528957a Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Mon, 24 Nov 2014 15:37:36 +0100 Subject: [PATCH 31/54] Revert "certificate as attribute" This reverts commit e211f65872fb13b44e149b9983251db3edd020ad. --- lib/update/update.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/update/update.py b/lib/update/update.py index 29088ce..1532fbc 100755 --- a/lib/update/update.py +++ b/lib/update/update.py @@ -26,8 +26,7 @@ class Updater(object): update_url = "https://s.eff.org/files/openwireless/update.json.asc" sysupgrade_command = ["/usr/bin/sudo", "sysupgrade", "-v", "-n"] update_check_file = "/etc/last_update_check" - ca_file = "/etc/ssl/certs/StartCom_Certification_Authority.crt" - + def __init__(self): self.firmware = None self.current = None @@ -51,7 +50,7 @@ def get_manifest(self): curl.setopt(pycurl.PROXYPORT, 9050) curl.setopt(pycurl.PROXY, "localhost") curl.setopt(pycurl.PROXYTYPE, 6) # == PROXYTYPE_SOCKS4A - curl.setopt(pycurl.CAINFO, self.ca_file) + curl.setopt(pycurl.CAINFO, "/etc/ssl/certs/StartCom_Certification_Authority.crt") curl.setopt(pycurl.URL, self.update_url) curl.setopt(pycurl.WRITEFUNCTION, buffer.write) try: @@ -100,7 +99,7 @@ def download_file(self): curl = pycurl.Curl() curl.setopt(pycurl.URL, self.manifest["url"].encode("ascii")) curl.setopt(pycurl.WRITEFUNCTION, buffer.write) - curl.setopt(pycurl.CAINFO, self.ca_file) + curl.setopt(pycurl.CAINFO, "/etc/ssl/certs/StartCom_Certification_Authority.crt") try: curl.perform() except Exception, e: From c3f133dc7a6b56a4dfda8e62901ad57b917c7b26 Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Mon, 24 Nov 2014 15:37:41 +0100 Subject: [PATCH 32/54] Revert "fixes typo" This reverts commit c9e16ed3d615d8bc0afd3f91818669799f51526a. --- lib/update/update.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/update/update.py b/lib/update/update.py index 1532fbc..9457f09 100755 --- a/lib/update/update.py +++ b/lib/update/update.py @@ -182,9 +182,9 @@ def check_for_updates(self, advise_only=False): """ if not self.purported_manifest: print "Getting update metadata..." - if not self.get_manifest(): + if not u.get_manifest(): raise UpdateError("to download update metadata") - print self.purported_manifest + print u.purported_manifest if not self.manifest: print "Validating update signature..." From 584b56df618e03bfe497fd8e2b842487c74ee5b3 Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Mon, 24 Nov 2014 15:37:42 +0100 Subject: [PATCH 33/54] Revert "Code style improvement" This reverts commit c592d159653ddf9ada287233ab9da3985eec82b3. --- lib/update/update.py | 163 ++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 104 deletions(-) diff --git a/lib/update/update.py b/lib/update/update.py index 9457f09..d3244f6 100755 --- a/lib/update/update.py +++ b/lib/update/update.py @@ -4,54 +4,40 @@ import systemwide_lock import time +openwrt_release_file = "/etc/openwrt_release" +keyring = "/etc/update_key.gpg" +update_url = "https://s.eff.org/files/openwireless/update.json.asc" +sysupgrade_command = ["/usr/bin/sudo", "sysupgrade", "-v", "-n"] +update_check_file = "/etc/last_update_check" def failed(why): sys.stderr.write("Failed %s\n" % why) systemwide_lock.release_lock() sys.exit(1) -class UpdateError(Exception): - '''Exception raised if an error occures while updating. - ''' - def __init__(self, message): - self.message = message - - def __str__(self): - return ("Failed " + self.message) - class Updater(object): - - openwrt_release_file = "/etc/openwrt_release" - keyring = "/etc/update_key.gpg" - update_url = "https://s.eff.org/files/openwireless/update.json.asc" - sysupgrade_command = ["/usr/bin/sudo", "sysupgrade", "-v", "-n"] - update_check_file = "/etc/last_update_check" - def __init__(self): self.firmware = None self.current = None - with open(self.openwrt_release_file) as f: + with open(openwrt_release_file) as f: for line in f: m = re.match(r'^DISTRIB_RELEASE_DATE="(\d+)"$', line) if m and len(m.groups()) == 1: self.current = int(m.groups()[0]) break - if self.current is None: - raise UpdateError("to find current version in " + self.openwrt_release_file) + else: + failed("to find current version in /etc/openwrt_release") self.purported_manifest = None self.manifest = None def get_manifest(self): - '''Download the manifest from eff.org. - Returns True on success and False if the download failed. - ''' buffer = StringIO.StringIO() curl = pycurl.Curl() curl.setopt(pycurl.PROXYPORT, 9050) curl.setopt(pycurl.PROXY, "localhost") curl.setopt(pycurl.PROXYTYPE, 6) # == PROXYTYPE_SOCKS4A curl.setopt(pycurl.CAINFO, "/etc/ssl/certs/StartCom_Certification_Authority.crt") - curl.setopt(pycurl.URL, self.update_url) + curl.setopt(pycurl.URL, update_url) curl.setopt(pycurl.WRITEFUNCTION, buffer.write) try: curl.perform() @@ -67,8 +53,7 @@ def valid_sig(self): if not self.purported_manifest: return False in_fd, out_fd = os.pipe() - command = ["gpg", "--keyring", self.keyring, "--no-default-keyring", - "--status-fd", str(out_fd), "--decrypt"] + command = ["gpg", "--keyring", keyring, "--no-default-keyring", "--status-fd", str(out_fd), "--decrypt"] p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc_stdout, proc_stderr = p.communicate(self.purported_manifest) if p.returncode != 0: @@ -88,9 +73,6 @@ def valid_sig(self): return True def download_file(self): - '''Downloads the update image to a temporary file and saves its - location to the attribute `firmware`. - ''' if not self.manifest or not self.manifest.has_key("url"): return None url = self.manifest["url"] @@ -110,9 +92,7 @@ def download_file(self): return True def valid_firmware(self): - if not self.manifest: - return False - if not ("sha256" in self.manifest and "size" in self.manifest and "timestamp" in self.manifest): + if not (self.manifest.has_key("sha256") and self.manifest.has_key("size") and self.manifest.has_key("timestamp")): return False if not self.is_newer(): return False @@ -136,83 +116,28 @@ def do_update(self): # This is unlikely to actually return if successful, because the # sysupgrade command itself will likely kill the upgrader process # and also reboot the device. - return not subprocess.call(self.sysupgrade_command + [self.firmware]) + return not subprocess.call(sysupgrade_command + [self.firmware]) def extract_manifest(self, what): try: extracted_version = json.loads(what) if not isinstance(extracted_version, dict): - raise UpdateError("to extract a JSON-structured update manifest") + failed("to extract a JSON-structured update manifest") else: return extracted_version except ValueError, e: - raise UpdateError("to extract a JSON-structured update manifest") - raise UpdateError("not reached") + failed("to extract a JSON-structured update manifest") + failed("not reached") def parse_manifest(self): if not self.purported_manifest: - raise UpdateError("to get the manifest") + raise Exception, "cannot get manifest" if self.valid_sig(): self.manifest = self.extract_manifest(self.signed_manifest) return True else: return False - @staticmethod - def __update_check_file(newer): - """Store the current time (in Javascript format) in the file that - tracks when we last checked for updates. - """ - msg = "Updating " + self.update_check_file + " to indicate " - if not newer: - flag = " N" - msg += "no installable" - else: - flag = " Y" - msg += "an" - msg += " update is available." - with open(self.update_check_file, "w") as checkfile: - checkfile.write(repr(time.time()*1000) + flag) - return True - - def check_for_updates(self, advise_only=False): - """Checks for new software version. If advise_only is True it doesn't - call do_update() otherwise True is returned if a new firmware version - is available and False if it is the current. - """ - if not self.purported_manifest: - print "Getting update metadata..." - if not u.get_manifest(): - raise UpdateError("to download update metadata") - print u.purported_manifest - - if not self.manifest: - print "Validating update signature..." - if not self.parse_manifest(): - # Parsing includes signature validity checking - raise UpdateError("to validate signature of update metdata") - - print "Checking whether to update..." - newer = self.is_newer() - self.__update_check_file(newer) - if advise_only: - # In this case, the update script is being run merely to check - # and inform the user whether an update is available, not to - # install it. - return newer - - print "Downloading new firmware image..." - if not self.download_file(): - raise UpdateError("to download firmware image") - - print "Validating downloaded image..." - if not self.valid_firmware(): - raise UpdateError("to validate downloaded firmware image") - - print "Installing image..." - return self.do_update() - - if __name__ == '__main__': advise_only = False if len(sys.argv) > 1 and sys.argv[1] == 'check': @@ -222,22 +147,52 @@ def check_for_updates(self, advise_only=False): if not systemwide_lock.get_lock(): failed("to acquire update lock") u = Updater() - result = u.check_for_updates(advise_only) + print "Getting update metadata..." + if not u.get_manifest(): + failed("to download update metadata") + print u.purported_manifest + print "Validating update signature..." + if not u.parse_manifest(): # includes signature validity checking + failed("to validate signature of update metdata") + + print "Checking whether to update..." if advise_only: - # Release lock is called in the finally block - if result: - # update is available + # In this case, the update script is being run merely to check + # and inform the user whether an update is available, not to + # install it. + # Store the current time (in Javascript format) in the + # file that tracks when we last checked for updates. + if u.is_newer(): + print "Updating " + update_check_file + " to indicate update is available." + with open(update_check_file, "w") as f: + f.write(repr(time.time()*1000) + " Y") + systemwide_lock.release_lock() sys.exit(0) - # current version - sys.exit(1) + else: + print "Updating " + update_check_file + " to indicate no installable update available." + with open(update_check_file, "w") as f: + f.write(repr(time.time()*1000) + " N") + systemwide_lock.release_lock() + sys.exit(1) + if u.is_newer(): + print "Updating " + update_check_file + " to indicate update is available." + with open(update_check_file, "w") as f: + f.write(repr(time.time()*1000) + " Y") + print "Downloading new firmware image..." + if not u.download_file(): + failed("to download firmware image") + print "Validating downloaded image..." + if u.valid_firmware(): + print "Installing image..." + if not u.do_update(): + failed("to install update") + subprocess.call(["/usr/bin/sudo", "/sbin/reboot"]) + else: + failed("to validate downloaded firmware image") else: - if not result: - failed("to install update") - # Reboot if the installation completed but we're still alive - subprocess.call(["/usr/bin/sudo", "/sbin/reboot"]) - except UpdateError as why: - # Catch "trusted" exception - failed(why.message) + print "Updating " + update_check_file + " to indicate no installable update available." + with open(update_check_file, "w") as f: + f.write(repr(time.time()*1000) + " N") except Exception, e: print e failed("to update for an undetermined reason.") From a362dd4bb5b5c0869a778628123fd8a67f09e3ae Mon Sep 17 00:00:00 2001 From: c0ff3m4kr Date: Mon, 24 Nov 2014 15:37:43 +0100 Subject: [PATCH 34/54] Revert "Revert "test case"" This reverts commit 6f70f64726b4b3b5edb470fbf8b7e26ef1285d28. --- test/auth_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/auth_test.py b/test/auth_test.py index 6fd5dcc..e898305 100644 --- a/test/auth_test.py +++ b/test/auth_test.py @@ -58,6 +58,8 @@ def test_password(self): self.assertTrue(os.path.isfile(os.path.join(self.path, "password"))) self.assertTrue(self.auth.is_password("Passw0rd")) self.assertFalse(self.auth.is_password("badpass")) + self.assertFalse(self.auth.save_password("2Short")) + self.assertTrue(self.auth.is_password("Passw0rd")) def test_write(self): filename = os.path.join(self.path, "foo") From 2ed0dd98b53a59b57aa1b6a86ac04275bfac8f42 Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Mon, 24 Nov 2014 16:55:44 -0800 Subject: [PATCH 35/54] Add options to install-dev-dependencies.sh for whether to prompt and/or install Xvfb. --- Vagrantfile | 2 +- install-dev-dependencies.sh | 51 +++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index a61e591..a5ac0e2 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -15,7 +15,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.provision "shell", inline: < Date: Mon, 24 Nov 2014 17:18:50 -0800 Subject: [PATCH 36/54] Switch xvfb to use upstart...starts on reboot! --- infra/etc/init.d/Xvfb | 35 ----------------------------------- infra/etc/init/xvfb.conf | 12 ++++++++++++ install-dev-dependencies.sh | 4 ++-- 3 files changed, 14 insertions(+), 37 deletions(-) delete mode 100755 infra/etc/init.d/Xvfb create mode 100644 infra/etc/init/xvfb.conf diff --git a/infra/etc/init.d/Xvfb b/infra/etc/init.d/Xvfb deleted file mode 100755 index 08613f2..0000000 --- a/infra/etc/init.d/Xvfb +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -### BEGIN INIT INFO -# Provides: Xvfb -# Required-Start: $local_fs $remote_fs -# Required-Stop: -# X-Start-Before: -# Default-Start: 2 3 4 5 -# Default-Stop: -### END INIT INFO - -set -e -set -o nounset - -NAME=Xvfb -INIT=/etc/init.d/$NAME -PROGRAM=/usr/bin/$NAME -PIDFILE=/var/run/$NAME.pid -DISPLAY=:0 - -case "$1" in - start) -start-stop-daemon --oknodo --background --make-pidfile --start --exec $PROGRAM --pidfile $PIDFILE -- $DISPLAY -screen 0 1024x768x24 -;; - stop) -start-stop-daemon --oknodo --stop --pidfile $PIDFILE -rm $PIDFILE -;; - *) -echo "Usage: $INIT {start|stop}" >&2 -exit 1 -;; -esac - -exit 0 diff --git a/infra/etc/init/xvfb.conf b/infra/etc/init/xvfb.conf new file mode 100644 index 0000000..21a3805 --- /dev/null +++ b/infra/etc/init/xvfb.conf @@ -0,0 +1,12 @@ +# Xvfb - Xvfb virtual X windows + +description "Xvfb server" + +start on runlevel [2345] +stop on runlevel [!2345] + +respawn +respawn limit 10 5 +umask 022 + +exec /usr/bin/Xvfb :0 -screen 0 1024x768x24 diff --git a/install-dev-dependencies.sh b/install-dev-dependencies.sh index 1c2632b..5c79f8a 100755 --- a/install-dev-dependencies.sh +++ b/install-dev-dependencies.sh @@ -40,8 +40,8 @@ fi if [ -n "$INSTALL_XVFB" ]; then echo Okay, installing xvfb for you ':)' sudo apt-get install $ACCEPT_INSTALL_PROMPTS xvfb - sudo cp infra/etc/init.d/Xvfb /etc/init.d/Xvfb - sudo service Xvfb start + sudo cp infra/etc/init/xvfb.conf /etc/init/xvfb.conf + sudo service xvfb start fi cat < Date: Tue, 25 Nov 2014 00:42:36 -0800 Subject: [PATCH 37/54] Allow running the openwireless frontend w/ fake uci on vagrant box. OpenWireless requires the ability to modify permissions of files on disk. The vagrant mount '/vagrant' does not allow permissions to be modified, so this change mounts the running application in the temp filesystem. --- local-lighttpd/run-local-lighttpd.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/local-lighttpd/run-local-lighttpd.sh b/local-lighttpd/run-local-lighttpd.sh index 72ba1a5..7394d8b 100755 --- a/local-lighttpd/run-local-lighttpd.sh +++ b/local-lighttpd/run-local-lighttpd.sh @@ -3,10 +3,12 @@ DIR=$(dirname $0) cd $DIR HTTP_PORT=${HTTP_PORT:-8000} HTTPS_PORT=$(($HTTP_PORT + 1000)) -ETC=port-$HTTP_PORT-etc +ROOT=`mktemp -d /tmp/openwireless-frontend-XXXX` +ETC=$ROOT/port-$HTTP_PORT-etc rm -rf $ETC mkdir -m 0700 -p $ETC/auth mkdir -m 0700 -p $ETC/dropbear +cp -R *.py $ROOT echo '{ "sqm.ge00.download": "0", "sqm.ge00.upload": "0", From d5500000bebbd29b0bdfa396ca655553b759caf6 Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Tue, 25 Nov 2014 00:54:03 -0800 Subject: [PATCH 38/54] Updated README.md to include Vagrant documentation. --- README.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 697a506..6faee05 100644 --- a/README.md +++ b/README.md @@ -10,21 +10,50 @@ https://openwireless.org/. # Getting Started +## Ubuntu/Debian users: + Get the packages you need and install a git hook to run tests before push: - ./install-dev-dependencies.sh +``` +./install-dev-dependencies.sh +``` + +## Vagrant users: + +Requirements: + +* [vagrant 1.5+](https://www.vagrantup.com/) +* [virtualbox 4.3.12+](https://www.virtualbox.org/) + +Getting started with vagrant is done with: + +``` +vagrant up +``` + +You can then connect with the virtual machine with: + +``` +vagrant ssh +``` + +**NOTE: the project root is mounted at ```/vagrant```** + +Further instructions assume you are connected to the VM in the /vagrant directory. +## Boot frontend Try out the web UI locally: ./local-lighttpd/run-local-lighttpd.sh firefox http://localhost:8000/ +## Deploy changes to router Sync the web UI to your router: ./sendAppToRouter --continuous firefox http://gw.home.lan/ -# Running tests +## Running tests ./run-tests.sh From 52a987da0e266cf735c63b9c6967182476ea39d3 Mon Sep 17 00:00:00 2001 From: Sophie Krisch Date: Tue, 25 Nov 2014 11:50:39 -0800 Subject: [PATCH 39/54] Fixed #220 [Sophie, Shane] Updating the last-checked-date both when you are already up to date and when you successfully download an update. --- app/js/dashboard.js | 10 ++++++++-- app/js/update.js | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/app/js/dashboard.js b/app/js/dashboard.js index 84c6fa2..817f3e3 100644 --- a/app/js/dashboard.js +++ b/app/js/dashboard.js @@ -47,7 +47,12 @@ var dashboardModule = (function(){ var displayDate = function(lastCheckDate){ var m_names = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"); - var d = new Date(parseFloat(lastCheckDate)); + var d = lastCheckDate; + + if(!(d instanceof Date)) { + d = new Date(parseFloat(lastCheckDate)); + } + var curr_date = d.getDate(); var curr_month = d.getMonth(); var curr_year = d.getFullYear(); @@ -136,7 +141,8 @@ var dashboardModule = (function(){ }; return { - init: init + init: init, + displayDate: displayDate }; })(); diff --git a/app/js/update.js b/app/js/update.js index d59974a..a1be05b 100644 --- a/app/js/update.js +++ b/app/js/update.js @@ -2,6 +2,7 @@ var updateModule = (function(){ var checkUpdateCallback = function(response){ if(response.result.status == "up-to-date"){ + dashboardModule.displayDate(new Date()); alert("Your software is up-to-date!"); } else { if(confirm("A new version is available. Would you like to update now?")){ @@ -10,14 +11,29 @@ var updateModule = (function(){ } }; + var errorCallback = function(response){ + alert("Unable to update - check your internet connection"); + }; + + var updateCallback = function(response){ + if(response.result.status == "update-success"){ + dashboardModule.displayDate(new Date()); + alert("Successfully updated your software!"); + } else { + alert("Unable to complete update."); + } + }; + var checkUpdateRequest = { "url": "/cgi-bin/routerapi/check_updates", "successCallback": checkUpdateCallback, + "errorCallback": errorCallback, "data": {} }; var updateRequest = { "url": "/cgi-bin/routerapi/update", + "successCallback": updateCallback, "data": {} }; From 044c1d07f3ba0f23a40cfb766d7ce7b57a63a5b6 Mon Sep 17 00:00:00 2001 From: Steven Lowe Date: Tue, 25 Nov 2014 12:15:43 -0800 Subject: [PATCH 40/54] FIXED #211 - alignment issue with add ssh key controls and software version. --- app/css/admin-style.css | 7 ++++++- app/js/templates.js | 2 +- app/templates/settings.handlebars | 6 ++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/css/admin-style.css b/app/css/admin-style.css index c5d0524..bcb61ca 100644 --- a/app/css/admin-style.css +++ b/app/css/admin-style.css @@ -336,13 +336,18 @@ footer img { #enterSshKey { display: none; padding: 1em; + padding-bottom: 0; +} +#enterSshKey .controls { + margin: 0; + padding: 0; + text-align: right; } #input-SSH { width: 100%; height: 11em; } #cancel-SSH, #submit-SSH { - float: right; outline: medium none; background: none repeat scroll 0% 0% #00ADEE; border: 2px solid #00ADEE; diff --git a/app/js/templates.js b/app/js/templates.js index 7a35900..553f29b 100644 --- a/app/js/templates.js +++ b/app/js/templates.js @@ -201,7 +201,7 @@ function program1(depth0,data) { if (helper = helpers.contents) { stack1 = helper.call(depth0, {hash:{},data:data}); } else { helper = (depth0 && depth0.contents); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; } buffer += escapeExpression(stack1) - + "
\n \n \n \n "; + + "
\n
\n \n \n
\n \n "; return buffer; } function program2(depth0,data) { diff --git a/app/templates/settings.handlebars b/app/templates/settings.handlebars index d8e91c4..704f58b 100644 --- a/app/templates/settings.handlebars +++ b/app/templates/settings.handlebars @@ -27,8 +27,10 @@ after which it will be locked to prevent tampering. Further updates can be made via SSH login.
- - +
+ + +
{{/sshKey}}

From 4b57cf401dc67db52ec239f2f4f2a4eb3128f6b1 Mon Sep 17 00:00:00 2001 From: Sophie Krisch Date: Tue, 25 Nov 2014 12:36:43 -0800 Subject: [PATCH 41/54] FIXED #219 [Sophie, Shane] Remove the check for updates button when you click it --- app/css/admin-style.css | 20 ++++++++++++++++++-- app/css/style.css | 2 +- app/dashboard.html | 12 ++++++++++-- app/js/update.js | 2 ++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/app/css/admin-style.css b/app/css/admin-style.css index c5d0524..b6de3a5 100644 --- a/app/css/admin-style.css +++ b/app/css/admin-style.css @@ -1,5 +1,5 @@ @import url(oswald-font.css); -header, .network, footer { +header, .network, #check-for-updates div { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -312,7 +312,7 @@ h2 small { display: none; } -footer { +#check-for-updates div { -ms-flex-preferred-size: 44px; -webkit-flex-basis: 44px; flex-basis: 44px; @@ -356,3 +356,19 @@ label { padding-left: 1em; padding-bottom: 1em; } + +#check-for-updates .loading-message { + display: none; +} + +#check-for-updates .message { + display: flex; +} + +#check-for-updates.loading .loading-message { + display: flex; +} + +#check-for-updates.loading .message { + display: none; +} diff --git a/app/css/style.css b/app/css/style.css index 652b753..6788e99 100644 --- a/app/css/style.css +++ b/app/css/style.css @@ -147,4 +147,4 @@ p { .changePassword { display: none; -} +} \ No newline at end of file diff --git a/app/dashboard.html b/app/dashboard.html index f10cfe0..28e39e7 100644 --- a/app/dashboard.html +++ b/app/dashboard.html @@ -43,8 +43,16 @@

Dashboard

-

Update ...... : last checked ......

- update + +
+
+

Update ...... : last checked ......

+ update +
+ +

Loading...

+
+
WAN IP: LoadingLAN IP: Loading diff --git a/app/js/update.js b/app/js/update.js index a1be05b..01e191b 100644 --- a/app/js/update.js +++ b/app/js/update.js @@ -9,6 +9,7 @@ var updateModule = (function(){ requestModule.submitRequest(updateRequest); } } + $("#check-for-updates").removeClass("loading"); }; var errorCallback = function(response){ @@ -50,6 +51,7 @@ var updateModule = (function(){ $(function() { $('#checkForUpdate').click(function(){ + $("#check-for-updates").addClass("loading"); updateModule.submitUpdateRequest(); }); }); From a7f71b3a9f972bf2088d6cc02e4052a5450996a0 Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Wed, 26 Nov 2014 16:15:12 -0800 Subject: [PATCH 42/54] Extracted script to run just unit tests --- run-tests.sh | 2 +- scripts/unit | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100755 scripts/unit diff --git a/run-tests.sh b/run-tests.sh index 8a517d6..de34e48 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -20,7 +20,7 @@ if ! make assert_templates_js_up_to_date ; then exit 1 fi -/usr/bin/env python2.7 -m unittest discover -s test/ -p '*_test.py' +scripts/unit if which nodejs ; then NODEJS=nodejs else diff --git a/scripts/unit b/scripts/unit new file mode 100755 index 0000000..4ac60bd --- /dev/null +++ b/scripts/unit @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e + +/usr/bin/env python2.7 -m unittest discover -s test/ -p '*_test.py' From 97524cfa03f47c9b5e328828b85d7e13883e6f9e Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Fri, 28 Nov 2014 23:26:06 -0800 Subject: [PATCH 43/54] Removed global params dictionary, introduced a main method, errors return a 500. --- routerapi/update_setting | 108 ++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/routerapi/update_setting b/routerapi/update_setting index 5ce1c1c..b5d8d34 100755 --- a/routerapi/update_setting +++ b/routerapi/update_setting @@ -7,7 +7,6 @@ import common import uci environ = os.environ -params = {} band_5_channel_options = ['auto', '36 (5.180 GHz)', '40 (5.200 GHz)', '44 (5.220 GHz)', '48 (5.240 GHz)', '149 (5.745 GHz)', '153 (5.765 GHz)', '159 (5.785 GHz)', '161 (5.805 GHz)', @@ -37,30 +36,30 @@ def set_param(device, param, value): uci.commit("wireless") def set_error(param): - params['error'] = 'Invalid ' + param + raise Exception('Invalid ' + param) def check_band(band_name, interface_name): - if params.get(band_name): - if params.get(band_name) in ["2.4", "5"]: - current_band = check_device(interface_name) + if band_name: + if band_name in ["2.4", "5"]: + current_band = check_device(interface_name) - if current_band == "radio0": - new_band = "radio1" + if current_band == "radio0": + new_band = "radio1" - else: - new_band = "radio0" + else: + new_band = "radio0" - if new_band != check_param(interface_name, "device"): - set_param(interface_name, "device", new_band) - common.reset_wifi() + if new_band != check_param(interface_name, "device"): + set_param(interface_name, "device", new_band) + common.reset_wifi() - else: - set_error('band') + else: + set_error('band') def check_channel(channel_name, interface_name): - if params.get(channel_name): + if channel_name: current_device = check_device(interface_name) - new_channel = params.get(channel_name) + new_channel = channel_name if validate_channel(current_device, new_channel): if new_channel.split(' ')[0] != check_param(current_device, 'channel'): set_param(current_device, 'channel', new_channel.split(' ')[0]) @@ -70,9 +69,9 @@ def check_channel(channel_name, interface_name): set_error('channel') def check_channel_bandwidth(channel_name, interface_name): - if params.get(channel_name): + if channel_name: current_device = check_device(interface_name) - new_bandwidth = params.get(channel_name) + new_bandwidth = channel_name if validate_bandwidth(new_bandwidth): if check_param(current_device, 'htmode') != "HT" + new_bandwidth: set_param(current_device, 'htmode', "HT" + new_bandwidth) @@ -88,57 +87,60 @@ def set_openwireless_use_limit(option): uci.set(openwireless_interface, str(int(openwireless_bandwidth))) uci.commit("sqm") -def check_openwireless_bandwidth_percentage(): - if (params.get("openwirelessBandwidth")): - new_percentage = params.get("openwirelessBandwidth") - - uci.set("openwireless.maxbandwidthpercentage", new_percentage) +def check_openwireless_bandwidth_percentage(percentage): + if percentage: + uci.set("openwireless.maxbandwidthpercentage", percentage) uci.commit("openwireless") set_openwireless_use_limit("download") set_openwireless_use_limit("upload") -def check_isp_upload_speed(): - if (params.get("ispUploadSpeed")): - new_speed = float(params.get("ispUploadSpeed")) - new_speed_kbs = str(int(new_speed * 1000)) - uci.set("sqm.ge00.upload", new_speed_kbs) +def check_isp_upload_speed(speed): + if speed: + speed_kbs = str(int(float(speed) * 1000)) + uci.set("sqm.ge00.upload", speed_kbs) uci.commit("sqm") set_openwireless_use_limit("upload") -def check_isp_download_speed(): - if (params.get("ispDownloadSpeed")): - new_speed = float(params.get("ispDownloadSpeed")) - new_speed_kbs = str(int(new_speed * 1000)) - uci.set("sqm.ge00.download", new_speed_kbs) +def check_isp_download_speed(speed): + if speed: + speed_kbs = str(int(float(speed) * 1000)) + uci.set("sqm.ge00.download", speed_kbs) uci.commit("sqm") set_openwireless_use_limit("download") -def check_openwireless_monthly_data(): - if (params.get("openwirelessData")): - new_bandwidth = params.get("openwirelessData") - uci.set("openwireless.maxmonthlybandwidth", new_bandwidth) - uci.commit("openwireless") +def check_openwireless_monthly_data(bandwidth): + if bandwidth: + uci.set("openwireless.maxmonthlybandwidth", bandwidth) + uci.commit("openwireless") +def do_post(params): + check_band(params.get('routerBand'), "@wifi-iface[2]") + check_channel(params.get('routerChannel'), "@wifi-iface[2]") + check_channel_bandwidth(params.get('routerChannelBandwidth'), "@wifi-iface[2]") + check_band(params.get('openwirelessBand'), "@wifi-iface[1]") + check_channel(params.get('openwirelessChannel'), "@wifi-iface[1]") + check_channel_bandwidth(params.get('openwirelessChannelBandwidth'), "@wifi-iface[1]") -if environ.get('REQUEST_METHOD').lower() == 'post': - params = json.loads(sys.stdin.read()) + check_isp_upload_speed(params.get("ispUploadSpeed")) + check_isp_download_speed(params.get("ispDownloadSpeed")) - check_band('routerBand', "@wifi-iface[2]") - check_channel('routerChannel', "@wifi-iface[2]") - check_channel_bandwidth('routerChannelBandwidth', "@wifi-iface[0]") + check_openwireless_bandwidth_percentage(params.get("openwirelessBandwidth")) - check_band('openwirelessBand', "@wifi-iface[1]") - check_channel('openwirelessChannel', "@wifi-iface[1]") - check_channel_bandwidth('openwirelessChannelBandwidth', "@wifi-iface[1]") + check_openwireless_monthly_data(params.get("openwirelessData")) - check_isp_upload_speed() - check_isp_download_speed() - check_openwireless_bandwidth_percentage() - check_openwireless_monthly_data() -else: - set_error('GET request') +def main(): + try: + if environ.get('REQUEST_METHOD').lower() == 'post': + params = json.loads(sys.stdin.read()) + do_post(params) + else: + raise Exception('GET request') + common.render_success(params) + except Exception as e: + common.render_error(str.join(' ', e.args), status = 500) -common.render_success(params) +if __name__ == '__main__': + main() From 4327aa29c8dc2cbc6c2bc77566eb199426bb2c71 Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sat, 29 Nov 2014 18:08:42 -0800 Subject: [PATCH 44/54] Extracted FakeUci to separate class. --- test/fake_uci.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 test/fake_uci.py diff --git a/test/fake_uci.py b/test/fake_uci.py new file mode 100644 index 0000000..a78155d --- /dev/null +++ b/test/fake_uci.py @@ -0,0 +1,14 @@ +class FakeUci: + def __init__(self): + self.data = {} + self.tmp = {} + + def get(self, name): + return self.data[name] + + def set(self, name, value): + self.tmp[name] = value + + def commit(self, section): + self.data = dict(self.data.items() + self.tmp.items()) + self.tmp = {} From 2621ec6040dc2651291508b38f7331d4e618bbf8 Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sat, 29 Nov 2014 23:22:26 -0800 Subject: [PATCH 45/54] Update network availablility upon bandwidth change. --- routerapi/accumulate_bytes | 12 ++++++++---- routerapi/accumulate_bytes.py | 1 + routerapi/update_setting | 2 ++ test/update_setting_test.py | 21 +++++++++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) create mode 120000 routerapi/accumulate_bytes.py create mode 100644 test/update_setting_test.py diff --git a/routerapi/accumulate_bytes b/routerapi/accumulate_bytes index 01a3686..d12bd95 100644 --- a/routerapi/accumulate_bytes +++ b/routerapi/accumulate_bytes @@ -8,7 +8,7 @@ from subprocess import check_output from get_bytecounts import get_device_and_byte_counts -def disable_overutilized_network(): +def update_network_availability(): use_before_last_reset = float(uci.get("openwireless.use_before_last_reset")) use_since_last_reset = float(uci.get("openwireless.use_since_last_reset")) use_at_last_ui_reset = float(uci.get("openwireless.use_at_last_ui_reset")) @@ -62,6 +62,10 @@ def update_openwireless_usage(): else: increment_use_since_last_reset() -update_openwireless_usage() -disable_overutilized_network() -uci.commit("openwireless") +def main(): + update_openwireless_usage() + update_network_availability() + uci.commit("openwireless") + +if __name__ == '__main__': + main() diff --git a/routerapi/accumulate_bytes.py b/routerapi/accumulate_bytes.py new file mode 120000 index 0000000..d0897fd --- /dev/null +++ b/routerapi/accumulate_bytes.py @@ -0,0 +1 @@ +accumulate_bytes \ No newline at end of file diff --git a/routerapi/update_setting b/routerapi/update_setting index b5d8d34..ac5a663 100755 --- a/routerapi/update_setting +++ b/routerapi/update_setting @@ -5,6 +5,7 @@ import sys import common import uci +import accumulate_bytes environ = os.environ band_5_channel_options = ['auto', @@ -113,6 +114,7 @@ def check_openwireless_monthly_data(bandwidth): if bandwidth: uci.set("openwireless.maxmonthlybandwidth", bandwidth) uci.commit("openwireless") + accumulate_bytes.update_network_availability() def do_post(params): check_band(params.get('routerBand'), "@wifi-iface[2]") diff --git a/test/update_setting_test.py b/test/update_setting_test.py new file mode 100644 index 0000000..8f7b6f9 --- /dev/null +++ b/test/update_setting_test.py @@ -0,0 +1,21 @@ +import unittest +import mock +from fake_uci import FakeUci + +import update_setting + +class TestUpdateSetting(unittest.TestCase): + @mock.patch('accumulate_bytes.update_network_availability') + @mock.patch('update_setting.uci', new_callable = FakeUci) + def test_update_network_availability_after_monthly_data_limit_change(self, _, update_network_availability): + update_setting.check_openwireless_monthly_data('500') + update_network_availability.assert_called_with() + + @mock.patch('accumulate_bytes.update_network_availability') + @mock.patch('update_setting.uci', new_callable = FakeUci) + def test_update_openwireless_monthly_data(self, uci, _): + update_setting.check_openwireless_monthly_data('500') + + bandwidth = uci.get("openwireless.maxmonthlybandwidth") + self.assertEquals(bandwidth, '500') + From 77decea784e8fe5d12af3ee8a0bfee0e8d2dfe9c Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sun, 30 Nov 2014 19:37:07 -0800 Subject: [PATCH 46/54] Enable wireless router when current_use is below limit. --- routerapi/accumulate_bytes | 5 +++- test/accumulate_bytes_test.py | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 test/accumulate_bytes_test.py diff --git a/routerapi/accumulate_bytes b/routerapi/accumulate_bytes index d12bd95..ed8e2f6 100644 --- a/routerapi/accumulate_bytes +++ b/routerapi/accumulate_bytes @@ -18,7 +18,10 @@ def update_network_availability(): uci.set("wireless.@wifi-iface[1].disabled", "1") uci.commit("wireless") common.reset_wifi() - + if current_use < max_use: + uci.set("wireless.@wifi-iface[1].disabled", "0") + uci.commit("wireless") + common.reset_wifi() def update_openwireless_usage(): diff --git a/test/accumulate_bytes_test.py b/test/accumulate_bytes_test.py new file mode 100644 index 0000000..2088058 --- /dev/null +++ b/test/accumulate_bytes_test.py @@ -0,0 +1,45 @@ +import unittest +import mock +import sys +import os + +sys.path.insert(0, os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", "routerapi")) + +import accumulate_bytes +from fake_uci import FakeUci + +class AccumulateBytesTest(unittest.TestCase): + def with_usage_in_mb(self, uci, usage): + uci.set("openwireless.use_before_last_reset", "0") + uci.set("openwireless.use_since_last_reset", float(usage) * 1024 * 1024) + uci.set("openwireless.use_at_last_ui_reset", "0") + uci.commit("openwireless") + + @mock.patch('accumulate_bytes.common.reset_wifi') + @mock.patch('accumulate_bytes.uci', new_callable = FakeUci) + def test_turn_on_adapter_if_off_and_sufficient_bandwidth(self, uci, reset): + uci.set("openwireless.maxmonthlybandwidth", "20") + uci.set("wireless.@wifi-iface[1].disabled", "1") + self.with_usage_in_mb(uci, "0") + uci.commit("all") + + accumulate_bytes.update_network_availability() + + self.assertEquals(uci.get("wireless.@wifi-iface[1].disabled"), "0") + reset.assert_called_with() + + @mock.patch('accumulate_bytes.common.reset_wifi') + @mock.patch('accumulate_bytes.uci', new_callable = FakeUci) + def test_turn_off_adapter_if_on_and_insufficient_bandwidth(self, uci, reset): + self.with_usage_in_mb(uci, "20") + uci.set("openwireless.maxmonthlybandwidth", "20") + uci.set("wireless.@wifi-iface[1].disabled", "0") + uci.commit("all") + + accumulate_bytes.update_network_availability() + + self.assertEquals(uci.get("wireless.@wifi-iface[1].disabled"), "1") + reset.assert_called_with() + From 65ea99a1d8c77af4bf14ea46768fbaf756435196 Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Sun, 30 Nov 2014 23:18:18 -0800 Subject: [PATCH 47/54] Should not restart wifi if disabled state does not change. --- routerapi/accumulate_bytes | 10 ++++------ test/accumulate_bytes_test.py | 25 +++++++++++++++++++++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/routerapi/accumulate_bytes b/routerapi/accumulate_bytes index ed8e2f6..315dcb9 100644 --- a/routerapi/accumulate_bytes +++ b/routerapi/accumulate_bytes @@ -14,12 +14,10 @@ def update_network_availability(): use_at_last_ui_reset = float(uci.get("openwireless.use_at_last_ui_reset")) current_use = use_before_last_reset + use_since_last_reset - use_at_last_ui_reset max_use = float(uci.get("openwireless.maxmonthlybandwidth"))*1000000 - if current_use > max_use: - uci.set("wireless.@wifi-iface[1].disabled", "1") - uci.commit("wireless") - common.reset_wifi() - if current_use < max_use: - uci.set("wireless.@wifi-iface[1].disabled", "0") + current_state = uci.get("wireless.@wifi-iface[1].disabled") + new_state = "1" if current_use > max_use else "0" + if current_state != new_state: + uci.set("wireless.@wifi-iface[1].disabled", new_state) uci.commit("wireless") common.reset_wifi() diff --git a/test/accumulate_bytes_test.py b/test/accumulate_bytes_test.py index 2088058..4ae8b9d 100644 --- a/test/accumulate_bytes_test.py +++ b/test/accumulate_bytes_test.py @@ -10,6 +10,8 @@ import accumulate_bytes from fake_uci import FakeUci +@mock.patch('accumulate_bytes.common.reset_wifi') +@mock.patch('accumulate_bytes.uci', new_callable = FakeUci) class AccumulateBytesTest(unittest.TestCase): def with_usage_in_mb(self, uci, usage): uci.set("openwireless.use_before_last_reset", "0") @@ -17,8 +19,6 @@ def with_usage_in_mb(self, uci, usage): uci.set("openwireless.use_at_last_ui_reset", "0") uci.commit("openwireless") - @mock.patch('accumulate_bytes.common.reset_wifi') - @mock.patch('accumulate_bytes.uci', new_callable = FakeUci) def test_turn_on_adapter_if_off_and_sufficient_bandwidth(self, uci, reset): uci.set("openwireless.maxmonthlybandwidth", "20") uci.set("wireless.@wifi-iface[1].disabled", "1") @@ -30,8 +30,6 @@ def test_turn_on_adapter_if_off_and_sufficient_bandwidth(self, uci, reset): self.assertEquals(uci.get("wireless.@wifi-iface[1].disabled"), "0") reset.assert_called_with() - @mock.patch('accumulate_bytes.common.reset_wifi') - @mock.patch('accumulate_bytes.uci', new_callable = FakeUci) def test_turn_off_adapter_if_on_and_insufficient_bandwidth(self, uci, reset): self.with_usage_in_mb(uci, "20") uci.set("openwireless.maxmonthlybandwidth", "20") @@ -43,3 +41,22 @@ def test_turn_off_adapter_if_on_and_insufficient_bandwidth(self, uci, reset): self.assertEquals(uci.get("wireless.@wifi-iface[1].disabled"), "1") reset.assert_called_with() + def test_do_not_reset_when_not_changing_on_state(self, uci, reset): + self.with_usage_in_mb(uci, "20") + uci.set("openwireless.maxmonthlybandwidth", "40") + uci.set("wireless.@wifi-iface[1].disabled", "0") + uci.commit("all") + + accumulate_bytes.update_network_availability() + + self.assertFalse(reset.called) + + def test_do_not_reset_when_not_changing_off_state(self, uci, reset): + self.with_usage_in_mb(uci, "20") + uci.set("openwireless.maxmonthlybandwidth", "10") + uci.set("wireless.@wifi-iface[1].disabled", "1") + uci.commit("all") + + accumulate_bytes.update_network_availability() + + self.assertFalse(reset.called) From d983251b188d6fae985bb1a1770bb92efe286cad Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Mon, 1 Dec 2014 00:21:30 -0800 Subject: [PATCH 48/54] Fix fake_uci for multiple sections. --- test/accumulate_bytes_test.py | 12 ++++++--- test/fake_uci.py | 12 ++++++--- test/fake_uci_test.py | 46 +++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 test/fake_uci_test.py diff --git a/test/accumulate_bytes_test.py b/test/accumulate_bytes_test.py index 4ae8b9d..066efac 100644 --- a/test/accumulate_bytes_test.py +++ b/test/accumulate_bytes_test.py @@ -23,7 +23,8 @@ def test_turn_on_adapter_if_off_and_sufficient_bandwidth(self, uci, reset): uci.set("openwireless.maxmonthlybandwidth", "20") uci.set("wireless.@wifi-iface[1].disabled", "1") self.with_usage_in_mb(uci, "0") - uci.commit("all") + uci.commit("openwireless") + uci.commit("wireless") accumulate_bytes.update_network_availability() @@ -34,7 +35,8 @@ def test_turn_off_adapter_if_on_and_insufficient_bandwidth(self, uci, reset): self.with_usage_in_mb(uci, "20") uci.set("openwireless.maxmonthlybandwidth", "20") uci.set("wireless.@wifi-iface[1].disabled", "0") - uci.commit("all") + uci.commit("openwireless") + uci.commit("wireless") accumulate_bytes.update_network_availability() @@ -45,7 +47,8 @@ def test_do_not_reset_when_not_changing_on_state(self, uci, reset): self.with_usage_in_mb(uci, "20") uci.set("openwireless.maxmonthlybandwidth", "40") uci.set("wireless.@wifi-iface[1].disabled", "0") - uci.commit("all") + uci.commit("openwireless") + uci.commit("wireless") accumulate_bytes.update_network_availability() @@ -55,7 +58,8 @@ def test_do_not_reset_when_not_changing_off_state(self, uci, reset): self.with_usage_in_mb(uci, "20") uci.set("openwireless.maxmonthlybandwidth", "10") uci.set("wireless.@wifi-iface[1].disabled", "1") - uci.commit("all") + uci.commit("openwireless") + uci.commit("wireless") accumulate_bytes.update_network_availability() diff --git a/test/fake_uci.py b/test/fake_uci.py index a78155d..b860e5b 100644 --- a/test/fake_uci.py +++ b/test/fake_uci.py @@ -1,6 +1,6 @@ class FakeUci: - def __init__(self): - self.data = {} + def __init__(self, data={}): + self.data = data self.tmp = {} def get(self, name): @@ -10,5 +10,9 @@ def set(self, name, value): self.tmp[name] = value def commit(self, section): - self.data = dict(self.data.items() + self.tmp.items()) - self.tmp = {} + match = lambda x: x[0].startswith(section + ".") + tmp_section_items = [i for i in self.tmp.items() if match(i)] + non_tmp_section_items = [i for i in self.tmp.items() if not match(i)] + + self.data = dict(self.data.items() + tmp_section_items) + self.tmp = dict(non_tmp_section_items) diff --git a/test/fake_uci_test.py b/test/fake_uci_test.py new file mode 100644 index 0000000..f98a787 --- /dev/null +++ b/test/fake_uci_test.py @@ -0,0 +1,46 @@ +import unittest + +from fake_uci import FakeUci + +class FakeUciTest(unittest.TestCase): + def test_get_when_key_exists(self): + uci = FakeUci(data = {'section.key': 1}) + + self.assertEquals(1, uci.get('section.key')) + + def test_get_when_key_missing(self): + uci = FakeUci(data = {}) + + self.assertRaises(KeyError, uci.get, ('section.key')) + + def test_set_without_commit(self): + uci = FakeUci() + + uci.set('section.key', 1) + + self.assertRaises(KeyError, uci.get, ('section.key')) + + def test_set_with_commit(self): + uci = FakeUci() + + uci.set('section.key', 1) + uci.commit('section') + + self.assertEquals(1, uci.get('section.key')) + + def test_set_with_commit_on_different_section(self): + uci = FakeUci() + + uci.set('section.key', 1) + uci.commit('section1') + + self.assertRaises(KeyError, uci.get, ('section.key')) + + def test_set_with_commit_on_section_after_commit_on_different_section(self): + uci = FakeUci() + + uci.set('section.key', 1) + uci.commit('section1') + uci.commit('section') + + self.assertEquals(1, uci.get('section.key')) From 06cd525c0e3943bf472402cb5738d3179012470a Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Mon, 1 Dec 2014 11:05:47 -0800 Subject: [PATCH 49/54] Remove unused lighttpd/conf.d directories. --- etc/lighttpd/conf.d/10-alias.conf | 1 - etc/lighttpd/conf.d/10-cgi.conf | 33 ------ etc/lighttpd/conf.d/10-expire.conf | 22 ---- etc/lighttpd/conf.d/10-fastcgi.conf | 135 ----------------------- etc/lighttpd/conf.d/10-redirect.conf | 1 - etc/lighttpd/conf.d/10-simple_vhost.conf | 28 ----- 6 files changed, 220 deletions(-) delete mode 100644 etc/lighttpd/conf.d/10-alias.conf delete mode 100644 etc/lighttpd/conf.d/10-cgi.conf delete mode 100644 etc/lighttpd/conf.d/10-expire.conf delete mode 100644 etc/lighttpd/conf.d/10-fastcgi.conf delete mode 100644 etc/lighttpd/conf.d/10-redirect.conf delete mode 100644 etc/lighttpd/conf.d/10-simple_vhost.conf diff --git a/etc/lighttpd/conf.d/10-alias.conf b/etc/lighttpd/conf.d/10-alias.conf deleted file mode 100644 index def97c3..0000000 --- a/etc/lighttpd/conf.d/10-alias.conf +++ /dev/null @@ -1 +0,0 @@ -server.modules += ( "mod_alias" ) diff --git a/etc/lighttpd/conf.d/10-cgi.conf b/etc/lighttpd/conf.d/10-cgi.conf deleted file mode 100644 index ed27799..0000000 --- a/etc/lighttpd/conf.d/10-cgi.conf +++ /dev/null @@ -1,33 +0,0 @@ -####################################################################### -## -## CGI modules -## --------------- -## -## http://www.lighttpd.net/documentation/cgi.html -## -server.modules += ( "mod_cgi" ) - -## -## Plain old CGI handling -## -## For PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini. -## -cgi.assign = ( ".pl" => "/usr/bin/perl", - ".cgi" => "/usr/bin/perl", - ".rb" => "/usr/bin/ruby", - ".erb" => "/usr/bin/eruby", - ".py" => "/usr/bin/python" ) - -## -## to get the old cgi-bin behavior of apache -## -## Note: make sure that mod_alias is loaded if you uncomment the -## next line. (see modules.conf) -## -#alias.url += ( "/cgi-bin" => server_root + "/cgi-bin" ) -#$HTTP["url"] =~ "^/cgi-bin" { -# cgi.assign = ( "" => "" ) -#} - -## -####################################################################### diff --git a/etc/lighttpd/conf.d/10-expire.conf b/etc/lighttpd/conf.d/10-expire.conf deleted file mode 100644 index 0ca0ab2..0000000 --- a/etc/lighttpd/conf.d/10-expire.conf +++ /dev/null @@ -1,22 +0,0 @@ -####################################################################### -## -## Expire Module -## --------------- -## -## See http://www.lighttpd.net/documentation/expire.html -## -server.modules += ( "mod_expire" ) - -## -## assignes a expiration to all files below the specified path. The -## specification of the time is made up of: -## -## -## -#expire.url = ( -# "/buggy/" => "access 2 hours", -# "/images/" => "access plus 1 seconds 2 minutes" -#) - -## -####################################################################### diff --git a/etc/lighttpd/conf.d/10-fastcgi.conf b/etc/lighttpd/conf.d/10-fastcgi.conf deleted file mode 100644 index 49cff62..0000000 --- a/etc/lighttpd/conf.d/10-fastcgi.conf +++ /dev/null @@ -1,135 +0,0 @@ -####################################################################### -## -## FastCGI Module -## --------------- -## -## http://www.lighttpd.net/documentation/fastcgi.html -## -server.modules += ( "mod_fastcgi" ) - -## -## PHP Example -## For PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini. -## -## The number of php processes you will get can be easily calculated: -## -## num-procs = max-procs * ( 1 + PHP_FCGI_CHILDREN ) -## -## for the php-num-procs example it means you will get 17*5 = 85 php -## processes. you always should need this high number for your very -## busy sites. And if you have a lot of RAM. :) -## -#fastcgi.server = ( ".php" => -# ( "php-local" => -# ( -# "socket" => socket_dir + "/php-fastcgi-1.socket", -# "bin-path" => server_root + "/cgi-bin/php5", -# "max-procs" => 1, -# "broken-scriptfilename" => "enable", -# ) -# ), -# ( "php-tcp" => -# ( -# "host" => "127.0.0.1", -# "port" => 9999, -# "check-local" => "disable", -# "broken-scriptfilename" => "enable", -# ) -# ), -# -# ( "php-num-procs" => -# ( -# "socket" => socket_dir + "/php-fastcgi-2.socket", -# "bin-path" => server_root + "/cgi-bin/php5", -# "bin-environment" => ( -# "PHP_FCGI_CHILDREN" => "16", -# "PHP_FCGI_MAX_REQUESTS" => "10000", -# ), -# "max-procs" => 5, -# "broken-scriptfilename" => "enable", -# ) -# ), -# ) - -## -## Ruby on Rails Example -## -## Normally you only run one Rails application on one vhost. -## -#$HTTP["host"] == "rails1.example.com" { -# server.document-root = server_root + "/rails/someapp/public" -# server.error-handler-404 = "/dispatch.fcgi" -# fastcgi.server = ( ".fcgi" => -# ("someapp" => -# ( "socket" => socket_dir + "/someapp-fcgi.socket", -# "bin-path" => server_root + "/rails/someapp/public/dispatch.fcgi", -# "bin-environment" => ( -# "RAILS_ENV" => "production", -# "TMP" => home_dir + "/rails/someapp", -# ), -# ) -# ) -# ) -#} - -## -## Another example with multiple rails applications on one vhost. -## -## http://blog.lighttpd.net/articles/2005/11/23/lighttpd-1-4-8-and-multiple-rails-apps -## -#$HTTP["host"] == "rails2.example.com" { -# $HTTP["url"] =~ "^/someapp1" { -# server.document-root = server_root + "/rails/someapp1/public" -# server.error-handler-404 = "/dispatch.fcgi" -# fastcgi.server = ( ".fcgi" => -# ("someapp1" => -# ( "socket" => socket_dir + "/someapp1-fcgi.socket", -# "bin-path" => server_root + "/rails/someapp1/public/dispatch.fcgi", -# "bin-environment" => ( -# "RAILS_ENV" => "production", -# "TMP" => home_dir + "/rails/someapp1", -# ), -# "strip-request-uri" => "/someapp1/" -# ) -# ) -# ) -# } -# -# $HTTP["url"] =~ "^/someapp2" { -# server.document-root = server_root + "/rails/someapp2/public" -# server.error-handler-404 = "/dispatch.fcgi" -# fastcgi.server = ( ".fcgi" => -# ("someapp2" => -# ( "socket" => socket_dir + "/someapp2-fcgi.socket", -# "bin-path" => server_root + "/rails/someapp2/public/dispatch.fcgi", -# "bin-environment" => ( -# "RAILS_ENV" => "production", -# "TMP" => home_dir + "/rails/someapp2", -# ), -# "strip-request-uri" => "/someapp2/" -# ) -# ) -# ) -# } -#} - -## chrooted webserver + external PHP -## -## $ spawn-fcgi -f /usr/bin/php-cgi -p 2000 -a 127.0.0.1 -C 8 -## -## webserver chrooted to /srv/www/ -## php running outside the chroot -# -#fastcgi.server = ( -# ".php" => (( -# "host" => "127.0.0.1", -# "port" => "2000", -# "docroot" => "/srv/www/servers/www.example.org/htdocs/" -# ))) -# -#server.chroot = "/srv/www" -#server.document-root = "/servers/wwww.example.org/htdocs/" -# - -## -####################################################################### diff --git a/etc/lighttpd/conf.d/10-redirect.conf b/etc/lighttpd/conf.d/10-redirect.conf deleted file mode 100644 index c022c09..0000000 --- a/etc/lighttpd/conf.d/10-redirect.conf +++ /dev/null @@ -1 +0,0 @@ -server.modules += ( "mod_redirect" ) diff --git a/etc/lighttpd/conf.d/10-simple_vhost.conf b/etc/lighttpd/conf.d/10-simple_vhost.conf deleted file mode 100644 index 220331e..0000000 --- a/etc/lighttpd/conf.d/10-simple_vhost.conf +++ /dev/null @@ -1,28 +0,0 @@ -####################################################################### -## -## Simple Virtual hosting -## ------------------------ -## -## http://www.lighttpd.net/documentation/simple-vhost.html -## -server.modules += ( "mod_simple_vhost" ) - -## If you want name-based virtual hosting add the next three settings and load -## mod_simple_vhost -## -## document-root = -## virtual-server-root + virtual-server-default-host + virtual-server-docroot -## or -## virtual-server-root + http-host + virtual-server-docroot -## -simple-vhost.server-root = vhosts_dir + "/" -simple-vhost.default-host = "default.example.com" -simple-vhost.document-root = "/htdocs/" - -## -## Print some errors for finding the document-root -## -#simple-vhost.debug = "enable" - -## -####################################################################### From 494be5081fdef18e44fb626aeac9d20ad4eea6ab Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Mon, 1 Dec 2014 12:26:28 -0800 Subject: [PATCH 50/54] Consolidated lighttpd config for local development and router. --- .../lighttpd.conf.template | 35 ++++---- lighttpd/router.properties | 10 +++ local-lighttpd/local-lighttpd.conf | 88 ------------------- local-lighttpd/run-local-lighttpd.sh | 30 ++++++- scripts/build-with-image-builder | 4 + scripts/template | 10 +++ sendToBuild | 4 + 7 files changed, 69 insertions(+), 112 deletions(-) rename etc/lighttpd/lighttpd.conf => lighttpd/lighttpd.conf.template (78%) create mode 100644 lighttpd/router.properties delete mode 100644 local-lighttpd/local-lighttpd.conf create mode 100755 scripts/template diff --git a/etc/lighttpd/lighttpd.conf b/lighttpd/lighttpd.conf.template similarity index 78% rename from etc/lighttpd/lighttpd.conf rename to lighttpd/lighttpd.conf.template index cd38c2c..d283b53 100644 --- a/etc/lighttpd/lighttpd.conf +++ b/lighttpd/lighttpd.conf.template @@ -15,25 +15,19 @@ server.modules = ( # force use of the "write" backend (closes: #2401) server.network-backend = "write" -server.document-root = "/www/" +server.document-root = "%(DOC_ROOT)s" # Disable symlinks to be paranoid. server.follow-symlink = "disable" index-file.names = ( "index.html" ) -alias.url += ( "/cgi-bin" => "/www/cgi-bin" ) +alias.url += ( "/cgi-bin" => "%(CGI_ROOT)s" ) $HTTP["url"] =~ "^/cgi-bin" { cgi.assign = ( "" => "" ) } -$HTTP["host"] == "172.30.42.1" { - url.redirect = ( - "^/(.*)" => "http://gw.home.lan/" - ) -} - #### SSL engine ### use PFS -# https://raymii.org/s/tutorials/Pass_the_SSL_Labs_Test_on_Lighttpd_%28Mitigate_the_CRIME_and_BEAST_attack_-_Disable_SSLv2_-_Enable_PFS%29.html +# https://raymii.org/s/tutorials/Pass_the_SSL_Labs_Test_on_Lighttpd_%%28Mitigate_the_CRIME_and_BEAST_attack_-_Disable_SSLv2_-_Enable_PFS%%29.html ssl.honor-cipher-order = "enable" ssl.cipher-list = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA" ssl.use-sslv2 = "disable" @@ -44,25 +38,24 @@ setenv.add-response-header = ( "X-Content-Type-Options" => "nosniff" ) -server.port = 80 -server.bind = "172.30.42.1" +server.port = %(HTTP_PORT)s +server.bind = "%(BIND)s" # When accessing on port 80, show the welcome page and warn the user that they # are about to get a cert warning. -$SERVER["socket"] == "172.30.42.1:80" { +$SERVER["socket"] == "%(BIND)s:%(HTTP_PORT)s" { index-file.names = ( "welcome.html" ) - # However, if they are logged in, take them straight to the HTTPS side of - # things. + $HTTP["cookie"] =~ "logged_in" { url.redirect = ( - "^/(.*)" => "https://gw.home.lan/" + "^/(.*)" => "https://%(APPLICATION_URL)s$0" ) } } -$SERVER["socket"] == "172.30.42.1:443" { +$SERVER["socket"] == "%(BIND)s:%(HTTPS_PORT)s" { ssl.engine = "enable" - ssl.pemfile = "/etc/lighttpd/lighttpd.pem" + ssl.pemfile = "%(PEM_FILE)s" } ## mimetype mapping @@ -87,7 +80,7 @@ mimetype.assign = ( ) ## to help the rc.scripts -server.pid-file = "/var/run/lighttpd.pid" +server.pid-file = "%(PID_FILE)s" ## virtual directory listings server.dir-listing = "enable" @@ -95,7 +88,9 @@ server.dir-listing = "enable" ## change uid to (default: don't care) # We'd like to run the server as root, but a couple of commands # (ping and iptaccount) won't run as user www-data. -server.username = "www-data" -server.groupname = "www-data" +server.username = "%(USER)s" +server.groupname = "%(GROUP)s" server.upload-dirs = ( "/tmp" ) + +include "overrides.conf" diff --git a/lighttpd/router.properties b/lighttpd/router.properties new file mode 100644 index 0000000..b810001 --- /dev/null +++ b/lighttpd/router.properties @@ -0,0 +1,10 @@ +DOC_ROOT=/www +CGI_ROOT=/www/cgi-bin +HTTP_PORT=80 +HTTPS_PORT=433 +BIND=172.30.42.1 +PID_FILE=/var/run/lighttpd.pid +USER=www-data +GROUP=www-data +PEM_FILE=/etc/lighttpd/lighttpd.pem +APPLICATION_URL=gw.home.lan diff --git a/local-lighttpd/local-lighttpd.conf b/local-lighttpd/local-lighttpd.conf deleted file mode 100644 index 226afe3..0000000 --- a/local-lighttpd/local-lighttpd.conf +++ /dev/null @@ -1,88 +0,0 @@ -# lighttpd configuration file for OpenWireless -# -## modules to load -# all other module should only be loaded if really neccesary -# - saves some time -# - saves memory -server.modules = ( - "mod_alias", - "mod_setenv", - "mod_simple_vhost", - "mod_cgi", - "mod_expire", -) - -# force use of the "write" backend (closes: #2401) -server.network-backend = "write" -server.document-root = "REPO_DIR/app" -server.follow-symlink = "disable" - -index-file.names = ( "index.html" ) -alias.url += ( "/cgi-bin/routerapi" => "REPO_DIR/routerapi" ) -$HTTP["url"] =~ "^/cgi-bin/routerapi" { - cgi.assign = ( "" => "" ) -} - -#### SSL engine -### use PFS -# https://raymii.org/s/tutorials/Pass_the_SSL_Labs_Test_on_Lighttpd_%28Mitigate_the_CRIME_and_BEAST_attack_-_Disable_SSLv2_-_Enable_PFS%29.html -ssl.honor-cipher-order = "enable" -ssl.cipher-list = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-RC4-SHA:ECDHE-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA" -ssl.use-sslv2 = "disable" - -setenv.add-response-header = ( - "Content-Security-Policy" => "default-src 'self'; img-src 'self' data:", - "X-Frame-Options" => "SAMEORIGIN", - "X-Content-Type-Options" => "nosniff" -) - -server.port = HTTP_PORT - -$SERVER["socket"] == ":HTTP_PORT" { - index-file.names = ( "welcome.html" ) -} - -$SERVER["socket"] == ":HTTPS_PORT" { - ssl.engine = "enable" - ssl.pemfile = "./local-lighttpd.pem" -} - -## mimetype mapping -mimetype.assign = ( - ".pdf" => "application/pdf", - ".class" => "application/octet-stream", - ".pac" => "application/x-ns-proxy-autoconfig", - ".swf" => "application/x-shockwave-flash", - ".wav" => "audio/x-wav", - ".gif" => "image/gif", - ".jpg" => "image/jpeg", - ".jpeg" => "image/jpeg", - ".png" => "image/png", - ".css" => "text/css", - ".html" => "text/html; charset=utf-8", - ".htm" => "text/html", - ".js" => "text/javascript", - ".txt" => "text/plain", - ".dtd" => "text/xml", - ".xml" => "text/xml", - ".svg" => "image/svg+xml" - ) - -## to help the rc.scripts -server.pid-file = "./local-lighttpd.pid" - -## virtual directory listings -server.dir-listing = "enable" - -## change uid to (default: don't care) -# We'd like to run the server as root, but a couple of commands -# (ping and iptaccount) won't run as user www-data. -#server.username = "www-data" -#server.groupname = "www-data" - -server.upload-dirs = ( "/tmp" ) - -setenv.add-environment = ( - "OVERRIDE_ETC" => var.CWD, - "OVERRIDE_PATH" => var.CWD + "/.." -) diff --git a/local-lighttpd/run-local-lighttpd.sh b/local-lighttpd/run-local-lighttpd.sh index 7394d8b..82d49bd 100755 --- a/local-lighttpd/run-local-lighttpd.sh +++ b/local-lighttpd/run-local-lighttpd.sh @@ -1,10 +1,14 @@ #!/bin/bash -ex -DIR=$(dirname $0) +DIR=$(cd `dirname $0` && pwd) cd $DIR HTTP_PORT=${HTTP_PORT:-8000} HTTPS_PORT=$(($HTTP_PORT + 1000)) ROOT=`mktemp -d /tmp/openwireless-frontend-XXXX` ETC=$ROOT/port-$HTTP_PORT-etc +REPO_DIR=`cd $DIR/.. && pwd` +DOC_ROOT=$REPO_DIR/app +CGI_ROOT=$REPO_DIR +LIGHTTPD_CONF_TEMPLATE=$REPO_DIR/lighttpd/lighttpd.conf.template rm -rf $ETC mkdir -m 0700 -p $ETC/auth mkdir -m 0700 -p $ETC/dropbear @@ -20,9 +24,27 @@ echo '{ "openwireless.use_at_last_ui_reset": "0", "openwireless.setup_state": "set-password" }' > $ETC/uci.json -cp local-lighttpd.pem $ETC/ +cp local-lighttpd.pem $ETC/lighttpd.pem -sed "s,REPO_DIR,$(readlink -f ..),; s,HTTP_PORT,$HTTP_PORT,; s,HTTPS_PORT,$HTTPS_PORT,;" local-lighttpd.conf > $ETC/local-lighttpd.conf +$REPO_DIR/scripts/template $LIGHTTPD_CONF_TEMPLATE > $ETC/lighttpd.conf < $ETC/overrides.conf < var.CWD, + "OVERRIDE_PATH" => var.CWD + "/.." +) +EOS cd $ETC echo "Access the web UI on http://localhost:$HTTP_PORT/" -exec lighttpd -D -f local-lighttpd.conf "$@" +exec lighttpd -D -f $ETC/lighttpd.conf "$@" diff --git a/scripts/build-with-image-builder b/scripts/build-with-image-builder index f8e0e7a..9c807ad 100755 --- a/scripts/build-with-image-builder +++ b/scripts/build-with-image-builder @@ -28,6 +28,10 @@ mkdir -p $FILES cp -rp $ROOT/etc $FILES/ +$ROOT/scripts/template $ROOT/lighttpd/lighttpd.conf.template > $FILES/etc/lighttpd/lighttpd.conf < $ROOT/lighttpd/router.properties + +touch $FILES/etc/lighttpd/overrides.conf + cp -rp $ROOT/lib $FILES/ mkdir $FILES/www diff --git a/scripts/template b/scripts/template new file mode 100755 index 0000000..1aadab3 --- /dev/null +++ b/scripts/template @@ -0,0 +1,10 @@ +#!/usr/bin/env python2.7 + +import sys + +replacements = dict(i.strip().split('=') for i in sys.stdin.readlines()) + +filename = sys.argv[1] + +with open(filename) as template: + print template.read() % replacements diff --git a/sendToBuild b/sendToBuild index 92497e6..c76739e 100755 --- a/sendToBuild +++ b/sendToBuild @@ -28,3 +28,7 @@ mkdir cerowrt/files/www/cgi-bin echo "pushing routerapi to build..." cp -rp routerapi cerowrt/files/www/cgi-bin/ +echo "building lighttpd config..." +cat lighttpd/router.properties | scripts/template lighttpd/lighttpd.conf.template > cerowrt/files/etc/lighttpd/lighttpd.conf + +touch cerowrt/files/etc/lighttpd/overrides.conf From 337340c17861d7b8ca65f6b0b0705a45225ec69c Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Mon, 1 Dec 2014 14:04:50 -0800 Subject: [PATCH 51/54] Use conf.d to organize lighttpd configuration, include local development overrides. --- lighttpd/lighttpd.conf.template | 2 +- local-lighttpd/run-local-lighttpd.sh | 3 ++- scripts/build-with-image-builder | 2 -- sendToBuild | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lighttpd/lighttpd.conf.template b/lighttpd/lighttpd.conf.template index d283b53..82cdbc7 100644 --- a/lighttpd/lighttpd.conf.template +++ b/lighttpd/lighttpd.conf.template @@ -93,4 +93,4 @@ server.groupname = "%(GROUP)s" server.upload-dirs = ( "/tmp" ) -include "overrides.conf" +include_shell "cat conf.d/*.conf 2> /dev/null" diff --git a/local-lighttpd/run-local-lighttpd.sh b/local-lighttpd/run-local-lighttpd.sh index 82d49bd..e64db33 100755 --- a/local-lighttpd/run-local-lighttpd.sh +++ b/local-lighttpd/run-local-lighttpd.sh @@ -39,7 +39,8 @@ PEM_FILE=$ETC/lighttpd.pem APPLICATION_URL=localhost:$HTTPS_PORT EOS -cat > $ETC/overrides.conf < $ETC/conf.d/local-test-server.conf < var.CWD, "OVERRIDE_PATH" => var.CWD + "/.." diff --git a/scripts/build-with-image-builder b/scripts/build-with-image-builder index 9c807ad..efb105e 100755 --- a/scripts/build-with-image-builder +++ b/scripts/build-with-image-builder @@ -30,8 +30,6 @@ cp -rp $ROOT/etc $FILES/ $ROOT/scripts/template $ROOT/lighttpd/lighttpd.conf.template > $FILES/etc/lighttpd/lighttpd.conf < $ROOT/lighttpd/router.properties -touch $FILES/etc/lighttpd/overrides.conf - cp -rp $ROOT/lib $FILES/ mkdir $FILES/www diff --git a/sendToBuild b/sendToBuild index c76739e..669d642 100755 --- a/sendToBuild +++ b/sendToBuild @@ -30,5 +30,3 @@ cp -rp routerapi cerowrt/files/www/cgi-bin/ echo "building lighttpd config..." cat lighttpd/router.properties | scripts/template lighttpd/lighttpd.conf.template > cerowrt/files/etc/lighttpd/lighttpd.conf - -touch cerowrt/files/etc/lighttpd/overrides.conf From 4a30580ecf3b90bc703876cdd170950723e3af5c Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Mon, 1 Dec 2014 14:47:11 -0800 Subject: [PATCH 52/54] Fix https port for router. --- lighttpd/router.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lighttpd/router.properties b/lighttpd/router.properties index b810001..d9c7de3 100644 --- a/lighttpd/router.properties +++ b/lighttpd/router.properties @@ -1,7 +1,7 @@ DOC_ROOT=/www CGI_ROOT=/www/cgi-bin HTTP_PORT=80 -HTTPS_PORT=433 +HTTPS_PORT=443 BIND=172.30.42.1 PID_FILE=/var/run/lighttpd.pid USER=www-data From 097ad48ae9a28b192e0921068d6e41ec23871871 Mon Sep 17 00:00:00 2001 From: Ranga Krishnan Date: Tue, 2 Dec 2014 15:29:11 -0800 Subject: [PATCH 53/54] Fixed config file to remove BUSYBOX TELNETD, TFTPD etc, removed unneeded packages to fix build hickups. This commit corresponds to release made today via openwireless.org --- OWrt/config-OWrt | 67 +++++++++++++++++++++++++++++---------------- etc/openwrt_release | 2 +- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/OWrt/config-OWrt b/OWrt/config-OWrt index febcbee..c0db554 100644 --- a/OWrt/config-OWrt +++ b/OWrt/config-OWrt @@ -1103,11 +1103,11 @@ CONFIG_BUSYBOX_DEFAULT_FEATURE_NTPD_SERVER=y CONFIG_BUSYBOX_DEFAULT_ROUTE=y # CONFIG_BUSYBOX_DEFAULT_SLATTACH is not set # CONFIG_BUSYBOX_DEFAULT_TCPSVD is not set -CONFIG_BUSYBOX_DEFAULT_TELNET=y -CONFIG_BUSYBOX_DEFAULT_FEATURE_TELNET_TTYPE=y +# CONFIG_BUSYBOX_DEFAULT_TELNET is not set +# CONFIG_BUSYBOX_DEFAULT_FEATURE_TELNET_TTYPE is not set # CONFIG_BUSYBOX_DEFAULT_FEATURE_TELNET_AUTOLOGIN is not set -CONFIG_BUSYBOX_DEFAULT_TELNETD=y -CONFIG_BUSYBOX_DEFAULT_FEATURE_TELNETD_STANDALONE=y +# CONFIG_BUSYBOX_DEFAULT_TELNETD is not set +# CONFIG_BUSYBOX_DEFAULT_FEATURE_TELNETD_STANDALONE is not set # CONFIG_BUSYBOX_DEFAULT_FEATURE_TELNETD_INETD_WAIT is not set # CONFIG_BUSYBOX_DEFAULT_TFTP is not set # CONFIG_BUSYBOX_DEFAULT_TFTPD is not set @@ -1911,11 +1911,11 @@ CONFIG_BUSYBOX_CONFIG_FEATURE_NTPD_SERVER=y CONFIG_BUSYBOX_CONFIG_ROUTE=y # CONFIG_BUSYBOX_CONFIG_SLATTACH is not set # CONFIG_BUSYBOX_CONFIG_TCPSVD is not set -CONFIG_BUSYBOX_CONFIG_TELNET=y -CONFIG_BUSYBOX_CONFIG_FEATURE_TELNET_TTYPE=y +# CONFIG_BUSYBOX_CONFIG_TELNET is not set +# CONFIG_BUSYBOX_CONFIG_FEATURE_TELNET_TTYPE is not set # CONFIG_BUSYBOX_CONFIG_FEATURE_TELNET_AUTOLOGIN is not set -CONFIG_BUSYBOX_CONFIG_TELNETD=y -CONFIG_BUSYBOX_CONFIG_FEATURE_TELNETD_STANDALONE=y +# CONFIG_BUSYBOX_CONFIG_TELNETD is not set +# CONFIG_BUSYBOX_CONFIG_FEATURE_TELNETD_STANDALONE is not set # CONFIG_BUSYBOX_CONFIG_FEATURE_TELNETD_INETD_WAIT is not set # CONFIG_BUSYBOX_CONFIG_TFTP is not set # CONFIG_BUSYBOX_CONFIG_TFTPD is not set @@ -2047,6 +2047,27 @@ CONFIG_BUSYBOX_CONFIG_SH_MATH_SUPPORT_64=y # CONFIG_BUSYBOX_CONFIG_SYSLOGD is not set # CONFIG_BUSYBOX_CONFIG_KLOGD is not set CONFIG_BUSYBOX_CONFIG_LOGGER=y + +# +#zoneinfo +# + +CONFIG_PACKAGE_zoneinfo-africa=y +CONFIG_PACKAGE_zoneinfo-asia=y +CONFIG_PACKAGE_zoneinfo-atlantic=y +CONFIG_PACKAGE_zoneinfo-australia-nz=y +CONFIG_PACKAGE_zoneinfo-core=y +CONFIG_PACKAGE_zoneinfo-europe=y +CONFIG_PACKAGE_zoneinfo-india=y +CONFIG_PACKAGE_zoneinfo-middleeast=y +CONFIG_PACKAGE_zoneinfo-northamerica=y +CONFIG_PACKAGE_zoneinfo-pacific=y +CONFIG_PACKAGE_zoneinfo-poles=y +CONFIG_PACKAGE_zoneinfo-simple=y +CONFIG_PACKAGE_zoneinfo-southamerica=y + + + CONFIG_PACKAGE_ca-certificates=y # CONFIG_PACKAGE_dnsmasq is not set # CONFIG_PACKAGE_dnsmasq-dhcpv6 is not set @@ -3157,7 +3178,7 @@ CONFIG_PACKAGE_in-jabberd=m # CONFIG_PACKAGE_ncat-ssl is not set # CONFIG_PACKAGE_ndiff is not set # CONFIG_PACKAGE_nmap is not set -CONFIG_PACKAGE_nmap-ssl=m +# CONFIG_PACKAGE_nmap-ssl is not set # CONFIG_PACKAGE_nping is not set # @@ -3523,7 +3544,7 @@ CONFIG_PACKAGE_authsae=m CONFIG_PACKAGE_bing=m # CONFIG_PACKAGE_bismark-ditg is not set CONFIG_PACKAGE_bwping=m -CONFIG_PACKAGE_ccnx=m +# CONFIG_PACKAGE_ccnx is not set CONFIG_PACKAGE_chat=y CONFIG_PACKAGE_debloat=y CONFIG_PACKAGE_ds-lite=y @@ -3628,8 +3649,8 @@ CONFIG_PACKAGE_tor=y # CONFIG_PACKAGE_tor-alpha is not set # CONFIG_PACKAGE_tor-alpha-fw-helper is not set # CONFIG_PACKAGE_tor-alpha-geoip is not set -CONFIG_PACKAGE_tor-fw-helper=m -CONFIG_PACKAGE_tor-geoip=m +# CONFIG_PACKAGE_tor-fw-helper is not set +# CONFIG_PACKAGE_tor-geoip is not set # CONFIG_PACKAGE_uclient-fetch is not set # CONFIG_PACKAGE_uqmi is not set CONFIG_PACKAGE_vnstat=m @@ -3741,7 +3762,7 @@ CONFIG_PACKAGE_bash=m # CONFIG_PACKAGE_bismark-netexp is not set # CONFIG_PACKAGE_bzip2 is not set # CONFIG_PACKAGE_cal is not set -CONFIG_PACKAGE_cerowrt-chrome=y +# CONFIG_PACKAGE_cerowrt-chrome is not set CONFIG_PACKAGE_cerowrt-interfaces=y CONFIG_PACKAGE_cerowrt-scripts=y CONFIG_PACKAGE_collectd=m @@ -3807,7 +3828,7 @@ CONFIG_PACKAGE_dbus=y CONFIG_PACKAGE_fconfig=m # CONFIG_PACKAGE_flock is not set # CONFIG_PACKAGE_getopt is not set -CONFIG_PACKAGE_gnugol=m +# CONFIG_PACKAGE_gnugol is not set CONFIG_PACKAGE_gnupg=y CONFIG_PACKAGE_haserl=m # CONFIG_HASERL_with_lua is not set @@ -3845,9 +3866,9 @@ CONFIG_PACKAGE_libjson-script=y CONFIG_PACKAGE_mount-utils=y # CONFIG_PACKAGE_mountd is not set # CONFIG_PACKAGE_namei is not set -CONFIG_PACKAGE_nut=m -CONFIG_PACKAGE_nut-client=m -CONFIG_PACKAGE_nut-driver-apcsmart=m +# CONFIG_PACKAGE_nut is not set +# CONFIG_PACKAGE_nut-client is not set +# CONFIG_PACKAGE_nut-driver-apcsmart is not set # CONFIG_PACKAGE_nut-driver-apcsmart-old is not set # CONFIG_PACKAGE_nut-driver-bcmxcp is not set # CONFIG_PACKAGE_nut-driver-bcmxcp_usb is not set @@ -3859,10 +3880,10 @@ CONFIG_PACKAGE_nut-driver-apcsmart=m # CONFIG_PACKAGE_nut-driver-richcomm_usb is not set # CONFIG_PACKAGE_nut-driver-snmp-ups is not set # CONFIG_PACKAGE_nut-driver-tripplite_usb is not set -CONFIG_PACKAGE_nut-driver-usbhid-ups=m -CONFIG_PACKAGE_nut-logger=m -CONFIG_PACKAGE_nut-monitor=m -CONFIG_PACKAGE_nut-server=m +# CONFIG_PACKAGE_nut-driver-usbhid-ups is not set +# CONFIG_PACKAGE_nut-logger is not set +# CONFIG_PACKAGE_nut-monitor is not set +# CONFIG_PACKAGE_nut-server is not set # CONFIG_PACKAGE_nut-driver-bestfcom is not set # CONFIG_PACKAGE_nut-driver-bestfortress is not set # CONFIG_PACKAGE_nut-driver-bestuferrups is not set @@ -3872,7 +3893,7 @@ CONFIG_PACKAGE_nut-server=m # CONFIG_PACKAGE_nut-driver-etapro is not set # CONFIG_PACKAGE_nut-driver-everups is not set # CONFIG_PACKAGE_nut-driver-gamatronic is not set -CONFIG_PACKAGE_nut-driver-genericups=m +# CONFIG_PACKAGE_nut-driver-genericups is not set # CONFIG_PACKAGE_nut-driver-isbmex is not set # CONFIG_PACKAGE_nut-driver-liebert is not set # CONFIG_PACKAGE_nut-driver-liebert-esp2 is not set @@ -3885,7 +3906,7 @@ CONFIG_PACKAGE_nut-driver-genericups=m # CONFIG_PACKAGE_nut-driver-oneac is not set # CONFIG_PACKAGE_nut-driver-optiups is not set # CONFIG_PACKAGE_nut-driver-powercom is not set -CONFIG_PACKAGE_nut-driver-powerpanel=m +# CONFIG_PACKAGE_nut-driver-powerpanel is not set # CONFIG_PACKAGE_nut-driver-rhino is not set # CONFIG_PACKAGE_nut-driver-safenet is not set # CONFIG_PACKAGE_nut-driver-solis is not set diff --git a/etc/openwrt_release b/etc/openwrt_release index 925bd93..a43eee7 100644 --- a/etc/openwrt_release +++ b/etc/openwrt_release @@ -4,5 +4,5 @@ DISTRIB_TAINTS="no-all busybox" DISTRIB_CODENAME="MVP-HOPE" DISTRIB_TARGET="ar71xx/generic" DISTRIB_REVISION="r41336" -DISTRIB_RELEASE_DATE="1405377121" +DISTRIB_RELEASE_DATE="1417020711" DISTRIB_DESCRIPTION="OWrt MVP-HOPE r41336 3.10.44-6" From f598d58a7c3f7166ccd9535fbe0d83f47960534b Mon Sep 17 00:00:00 2001 From: Andrew Kiellor Date: Wed, 3 Dec 2014 01:33:27 -0800 Subject: [PATCH 54/54] Fix load path for update_setting_test. --- test/update_setting_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/update_setting_test.py b/test/update_setting_test.py index 8f7b6f9..da14de0 100644 --- a/test/update_setting_test.py +++ b/test/update_setting_test.py @@ -1,7 +1,13 @@ import unittest +import sys +import os import mock from fake_uci import FakeUci +sys.path.insert(0, os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", "routerapi")) + import update_setting class TestUpdateSetting(unittest.TestCase):