diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e448ff..117f482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # Changelog -## V1.1.6 -- Reworked the navbar header to look better on smaller screens +## V1.2.0 + - Implemented IP/Subnet filter using the config option `ALLOWED_IPS` + - Implemented Password authentication of the site and API using config option `PASSWORD` + - Implemented max attachment size as mentioned in [#63](https://github.com/HaschekSolutions/opentrashmail/issues/63) + - Reworked the navbar header to look better on smaller screens ## V1.1.5 - Added support for plaintext file attachments diff --git a/README.md b/README.md index 3699b37..983dffd 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ - Web interface to manage emails - Generates random email addresses - 100% file based, no database needed -- Can be used as Email Honeypot +- Can be used as Email Honeypot or to programmatically solve 2fa emails # General API calls and functions @@ -64,6 +64,9 @@ Just edit the `config.ini` You can use the following settings - `MAILPORT`-> The port the Python-powered SMTP server will listen on. `Default: 25` - `ADMIN` -> An email address (doesn't have to exist, just has to be valid) that will list all emails of all addresses the server has received. Kind of a catch-all - `DATEFORMAT` -> How should timestamps be shown on the web interface ([moment.js syntax](https://momentjs.com/docs/#/displaying/)) +- `PASSWORD` -> If configured, site and API can't be used without providing it via form, POST/GET variable `password` or http header `PWD` (eg: `curl -H "PWD: 123456" http://localhost:8080/json...`) +- `ALLOWED_IPS` -> Comma separated list of IPv4 or IPv6 CIDR addresses that are allowed to use the web UI or API +- `ATTACHMENTS_MAX_SIZE` -> Max size for each individual attachment of an email in Bytes ## Docker env vars In Docker you can use the following environment variables: @@ -77,7 +80,9 @@ In Docker you can use the following environment variables: | ADMIN | If set to a valid email address and this address is entered in the API or webinterface, will show all emails of all accounts. Kind-of catch-all | test@test.com | DATEFORMAT | Will format the received date in the web interface based on [moment.js](https://momentjs.com/) syntax | "MMMM Do YYYY, h:mm:ss a" | | SKIP_FILEPERMISSIONS | If set to `true`, won't fix file permissions for the code data folder in the container. Useful for local dev. Default `false` | true,false | - +| PASSWORD | If configured, site and API can't be used without providing it via form, POST/GET variable `password` or http header `PWD` | yousrstrongpassword | +| ALLOWED_IPS | Comma separated list of IPv4 or IPv6 CIDR addresses that are allowed to use the web UI or API | `192.168.5.0/24,2a02:ab:cd:ef::/60,172.16.0.0/16` | +| ATTACHMENTS_MAX_SIZE | Max size for each individual attachment of an email in Bytes | `2000000` = 2MB | # Roadmap - [x] Mail server @@ -97,13 +102,13 @@ In Docker you can use the following environment variables: - [x] Make better theme - [x] Secure HTML, so no malicious things can be loaded - [x] Display embedded images inline using Content-ID -- [ ] Configurable settings +- [x] Configurable settings - [x] Choose domains for random generation - [x] Choose if out-of-scope emails are discarded - [x] Automated cleanup of old mails - - [ ] Honeypot mode where all emails are also saved for a catchall account - - [ ] Optionally secure whole site with a password - - [ ] Optionally allow site to be seen only from specific IP Range + - [x] Optionally secure whole site with a password + - [x] Optionally allow site to be seen only from specific IP Range + - [x] Honeypot mode where all emails are also saved for a catchall account (implemented with the ADMIN setting) # Quick start diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 46332f0..969dbc5 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -15,6 +15,9 @@ services: - DISCARD_UNKNOWN=false - SHOW_ACCOUNT_LIST=true - SHOW_LOGS=true + # - PASSWORD=123456 + # - ALLOWED_IPS=192.168.0.0/16,2a02:ab:cd:ef::/60 + # - ATTACHMENTS_MAX_SIZE=10000000 ports: - '2525:25' diff --git a/docker-compose.yml b/docker-compose.yml index 7f19327..319528d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,9 @@ services: - DATEFORMAT=D.M.YYYY HH:mm - SKIP_FILEPERMISSIONS=true - DISCARD_UNKNOWN=false + # - PASSWORD=123456 + # - ALLOWED_IPS=192.168.0.0/16,2a02:ab:cd:ef::/60 + # - ATTACHMENTS_MAX_SIZE=10000000 ports: - '2525:25' diff --git a/docker/rootfs/start.sh b/docker/rootfs/start.sh index c2c9cc0..94aaf8b 100644 --- a/docker/rootfs/start.sh +++ b/docker/rootfs/start.sh @@ -33,10 +33,13 @@ _buildConfig() { echo "SHOW_ACCOUNT_LIST=${SHOW_ACCOUNT_LIST:-false}" echo "ADMIN=${ADMIN:-}" echo "SHOW_LOGS=${SHOW_LOGS:-false}" + echo "PASSWORD=${PASSWORD:-}" + echo "ALLOWED_IPS=${ALLOWED_IPS:-}" echo "[MAILSERVER]" echo "MAILPORT=${MAILPORT:-25}" echo "DISCARD_UNKNOWN=${DISCARD_UNKNOWN:-true}" + echo "ATTACHMENTS_MAX_SIZE=${ATTACHMENTS_MAX_SIZE:-0}" echo "[DATETIME]" echo "DATEFORMAT=${DATEFORMAT:-D.M.YYYY HH:mm}" diff --git a/example.config.ini b/example.config.ini index cd32c8b..5949899 100644 --- a/example.config.ini +++ b/example.config.ini @@ -19,6 +19,14 @@ URL="http://localhost:8080" ; Enable to show logs on the website ;SHOW_LOGS=false +; Password authentication for Web UI and API +; Passwords have to be sent via the HTTP header "PWD" or as a GET/Post parameter "password" +;PASSWORD=mystrongpassword + +; If configured, only these IPs will be allowed to access the Web UI and API (but with no further authentication) +; Comma separated if multiple, can be IPv4 or IPv6 +;ALLOWED_IPS=192.168.0.0/16,2a02:ab:cd:ef::/60 + [MAILSERVER] ; Port that the Mailserver will run on (default 25 but that needs root) MAILPORT=25 @@ -27,6 +35,9 @@ MAILPORT=25 ; this greatly reduces the amount of spam you will receive DISCARD_UNKNOWN=true +; Limits the size of each attachment in bytes. Leave empty to disable +;ATTACHMENTS_MAX_SIZE=2000000 ; 2MB + ; Port number of the !! HIGHLY EXPERIMENTAL !! POP3 server ;POP3PORT=110 diff --git a/python/mailserver3.py b/python/mailserver3.py index 98027b2..7f54c56 100644 --- a/python/mailserver3.py +++ b/python/mailserver3.py @@ -17,6 +17,7 @@ logger = logging.getLogger(__name__) # globals for settings DISCARD_UNKNOWN = False DELETE_OLDER_THAN_DAYS = False +ATTACHMENTS_MAX_SIZE = 0 DOMAINS = [] LAST_CLEANUP = 0 URL = "" @@ -50,13 +51,19 @@ class CustomHandler: if part.get_content_type() == 'text/plain': #if it's a file if part.get_filename() is not None: - attachments['file%d' % len(attachments)] = self.handleAttachment(part) + att = self.handleAttachment(part) + if(att == False): + return '500 Attachment too large. Max size: ' + str(ATTACHMENTS_MAX_SIZE/1000000)+"MB" + attachments['file%d' % len(attachments)] = att else: plaintext += part.get_payload() elif part.get_content_type() == 'text/html': html += part.get_payload() else: - attachments['file%d' % len(attachments)] = self.handleAttachment(part) + att = self.handleAttachment(part) + if(att == False): + return '500 Attachment too large. Max size: ' + str(ATTACHMENTS_MAX_SIZE/1000000)+"MB" + attachments['file%d' % len(attachments)] = att for em in rcpts: em = em.lower() @@ -132,6 +139,10 @@ class CustomHandler: fid = hashlib.md5(filename.encode('utf-8')).hexdigest()+filename logger.debug('Handling attachment: "%s" (ID: "%s") of type "%s" with CID "%s"',filename, fid,part.get_content_type(), cid) + if(ATTACHMENTS_MAX_SIZE > 0 and len(part.get_payload(decode=True)) > ATTACHMENTS_MAX_SIZE): + logger.info("Attachment too large: " + filename) + return False + return (filename,part.get_payload(decode=True),cid,fid) def replace_cid_with_attachment_id(self, html_content, attachments,filenamebase,email): @@ -192,12 +203,15 @@ if __name__ == '__main__': DISCARD_UNKNOWN = (Config.get("MAILSERVER", "DISCARD_UNKNOWN").lower() == "true") DOMAINS = Config.get("GENERAL", "DOMAINS").lower().split(",") URL = Config.get("GENERAL", "URL") + if("attachments_max_size" in Config.options("MAILSERVER")): + ATTACHMENTS_MAX_SIZE = int(Config.get("MAILSERVER", "ATTACHMENTS_MAX_SIZE")) if("CLEANUP" in Config.sections() and "delete_older_than_days" in Config.options("CLEANUP")): DELETE_OLDER_THAN_DAYS = (Config.get("CLEANUP", "DELETE_OLDER_THAN_DAYS").lower() == "true") logger.info("[i] Starting Mailserver on port " + str(port)) logger.info("[i] Discard unknown domains: " + str(DISCARD_UNKNOWN)) + logger.info("[i] Max size of attachments: " + str(ATTACHMENTS_MAX_SIZE)) logger.info("[i] Listening for domains: " + str(DOMAINS)) asyncio.run(run(port)) diff --git a/web/inc/core.php b/web/inc/core.php index 89212cc..28f2ab2 100644 --- a/web/inc/core.php +++ b/web/inc/core.php @@ -186,6 +186,78 @@ function tailShell($filepath, $lines = 1) { return trim(ob_get_clean()); } +function getUserIP() +{ + if($_SERVER['HTTP_CF_CONNECTING_IP']) + return $_SERVER['HTTP_CF_CONNECTING_IP']; + $client = @$_SERVER['HTTP_CLIENT_IP']; + $forward = @$_SERVER['HTTP_X_FORWARDED_FOR']; + $remote = $_SERVER['REMOTE_ADDR']; + + if(strpos($forward,',')) + { + $a = explode(',',$forward); + $forward = trim($a[0]); + } + if(filter_var($forward, FILTER_VALIDATE_IP)) + { + $ip = $forward; + } + elseif(filter_var($client, FILTER_VALIDATE_IP)) + { + $ip = $client; + } + else + { + $ip = $remote; + } + return $ip; +} + +/** + * Check if a given IPv4 or IPv6 is in a network + * @param string $ip IP to check in IPV4 format eg. 127.0.0.1 + * @param string $range IP/CIDR netmask eg. 127.0.0.0/24, or 2001:db8::8a2e:370:7334/128 + * @return boolean true if the ip is in this range / false if not. + * via https://stackoverflow.com/a/56050595/1174516 + */ +function isIPInRange( $ip, $range ) { + + if(strpos($range,',')!==false) + { + // we got a list of ranges. splitting + $ranges = array_map('trim',explode(',',$range)); + foreach($ranges as $range) + if(isIPInRange($ip,$range)) return true; + return false; + } + // Get mask bits + list($net, $maskBits) = explode('/', $range); + + // Size + $size = (strpos($ip, ':') === false) ? 4 : 16; + + // Convert to binary + $ip = inet_pton($ip); + $net = inet_pton($net); + if (!$ip || !$net) { + throw new InvalidArgumentException('Invalid IP address'); + } + + // Build mask + $solid = floor($maskBits / 8); + $solidBits = $solid * 8; + $mask = str_repeat(chr(255), $solid); + for ($i = $solidBits; $i < $maskBits; $i += 8) { + $bits = max(0, min(8, $maskBits - $i)); + $mask .= chr((pow(2, $bits) - 1) << (8 - $bits)); + } + $mask = str_pad($mask, $size, chr(0)); + + // Compare the mask + return ($ip & $mask) === ($net & $mask); +} + function generateRandomEmail() { $nouns = ["aardvark","abyssinian","accelerator","accordion","account","accountant","acknowledgment","acoustic","acrylic","act","action","activity","actor","actress","adapter","addition","address","adjustment","adult","advantage","advertisement","aftermath","afternoon","aftershave","afterthought","age","agenda","agreement","air","airbus","airmail","airplane","airport","airship","alarm","albatross","alcohol","algebra","algeria","alibi","alley","alligator","alloy","almanac","alphabet","alto","aluminium","aluminum","ambulance","america","amount","amusement","anatomy","anethesiologist","anger","angle","angora","animal","anime","ankle","answer","ant","anteater","antelope","anthony","anthropology","apartment","apology","apparatus","apparel","appeal","appendix","apple","appliance","approval","april","aquarius","arch","archaeology","archeology","archer","architecture","area","argentina","argument","aries","arithmetic","arm","armadillo","armchair","army","arrow","art","ash","ashtray","asia","asparagus","asphalt","asterisk","astronomy","athlete","ATM","atom","attack","attempt","attention","attic","attraction","august","aunt","australia","australian","author","authority","authorization","avenue","baboon","baby","back","backbone","bacon","badge","badger","bag","bagel","bagpipe","bail","bait","baker","bakery","balance","balinese","ball","balloon","bamboo","banana","band","bandana","bangle","banjo","bank","bankbook","banker","bar","barbara","barber","barge","baritone","barometer","base","baseball","basement","basin","basket","basketball","bass","bassoon","bat","bath","bathroom","bathtub","battery","battle","bay","beach","bead","beam","bean","bear","beard","beast","beat","beautician","beauty","beaver","bed","bedroom","bee","beech","beef","beer","beet","beetle","beggar","beginner","begonia","behavior","belgian","belief","bell","belt","bench","bengal","beret","berry","bestseller","betty","bibliography","bicycle","bike","bill","billboard","biology","biplane","birch","bird","birth","birthday","bit","bite","black","bladder","blade","blanket","blinker","blizzard","block","blouse","blow","blowgun","blue","board","boat","bobcat","body","bolt","bomb","bomber","bone","bongo","bonsai","book","bookcase","booklet","boot","border","botany","bottle","bottom","boundary","bow","bowl","box","boy","bra","brace","bracket","brain","brake","branch","brand","brandy","brass","brazil","bread","break","breakfast","breath","brian","brick","bridge","british","broccoli","brochure","broker","bronze","brother","brother-in-law","brow","brown","brush","bubble","bucket","budget","buffer","buffet","bugle","building","bulb","bull","bulldozer","bumper","bun","burglar","burma","burn","burst","bus","bush","business","butane","butcher","butter","button","buzzard","cabbage","cabinet","cable","cactus","cafe","cake","calculator","calculus","calendar","calf","call","camel","camera","camp","can","cancer","candle","cannon","canoe","canvas","cap","capital","cappelletti","capricorn","captain","caption","car","caravan","carbon","card","cardboard","cardigan","care","carnation","carol","carp","carpenter","carriage","carrot","cart","cartoon","case","cast","castanet","cat","catamaran","caterpillar","cathedral","catsup","cattle","cauliflower","cause","caution","cave","c-clamp","cd","ceiling","celery","celeste","cell","cellar","cello","celsius","cement","cemetery","cent","centimeter","century","ceramic","cereal","certification","chain","chair","chalk","chance","change","channel","character","chard","charles","chauffeur","check","cheek","cheese","cheetah","chef","chemistry","cheque","cherry","chess","chest","chick","chicken","chicory","chief","child","children","chill","chime","chimpanzee","chin","china","chinese","chive","chocolate","chord","christmas","christopher","chronometer","church","cicada","cinema","circle","circulation","cirrus","citizenship","city","clam","clarinet","class","claus","clave","clef","clerk","click","client","climb","clipper","cloakroom","clock","close","closet","cloth","cloud","clover","club","clutch","coach","coal","coast","coat","cobweb","cockroach","cocktail","cocoa","cod","coffee","coil","coin","coke","cold","collar","college","collision","colombia","colon","colony","color","colt","column","columnist","comb","comfort","comic","comma","command","commission","committee","community","company","comparison","competition","competitor","composer","composition","computer","condition","condor","cone","confirmation","conga","congo","conifer","connection","consonant","continent","control","cook","copper","copy","copyright","cord","cork","cormorant","corn","cornet","correspondent","cost","cotton","couch","cougar","cough","country","course","court","cousin","cover","cow","cowbell","crab","crack","cracker","craftsman","crate","crawdad","crayfish","crayon","cream","creator","creature","credit","creditor","creek","crib","cricket","crime","criminal","crocodile","crocus","croissant","crook","crop","cross","crow","crowd","crown","crush","cry","cub","cuban","cucumber","cultivator","cup","cupboard","cupcake","curler","currency","current","curtain","curve","cushion","custard","customer","cut","cuticle","cycle","cyclone","cylinder","cymbal","dad","daffodil","dahlia","daisy","damage","dance","dancer","danger","daniel","dash","dashboard","database","date","daughter","david","day","dead","deadline","deal","death","deborah","debt","debtor","decade","december","decimal","decision","decrease","dedication","deer","defense","deficit","degree","delete","delivery","den","denim","dentist","deodorant","department","deposit","description","desert","design","desire","desk","dessert","destruction","detail","detective","development","dew","diamond","diaphragm","dibble","dictionary","dietician","difference","digestion","digger","digital","dill","dime","dimple","dinghy","dinner","dinosaur","diploma","dipstick","direction","dirt","disadvantage","discovery","discussion","disease","disgust","dish","distance","distribution","distributor","division","dock","doctor","dog","dogsled","doll","dollar","dolphin","domain","donald","donkey","donna","door","double","doubt","downtown","dragon","dragonfly","drain","drake","drama","draw","drawbridge","drawer","dream","dredger","dress","dresser","drill","drink","drive","driver","drizzle","drop","drug","drum","dry","dryer","duck","duckling","dugout","dungeon","dust","eagle","ear","earth","earthquake","ease","edge","edger","editor","editorial","education","edward","eel","effect","egg","eggnog","eggplant","egypt","eight","elbow","element","elephant","elizabeth","ellipse","emery","employee","employer","encyclopedia","end","enemy","energy","engine","engineer","english","enquiry","entrance","environment","epoch","epoxy","equinox","equipment","era","error","estimate","ethernet","ethiopia","euphonium","europe","evening","event","examination","example","exchange","exclamation","exhaust","ex-husband","existence","expansion","experience","expert","explanation","ex-wife","eye","eyebrow","eyelash","eyeliner","face","fact","factory","fahrenheit","fall","family","fan","fang","farm","farmer","fat","father","father-in-law","faucet","fear","feast","feather","feature","february","fedelini","feedback","feeling","feet","felony","female","fender","ferry","ferryboat","fertilizer","fiber","fiberglass","fibre","fiction","field","fifth","fight","fighter","file","find","fine","finger","fir","fire","fired","fireman","fireplace","firewall","fish","fisherman","flag","flame","flare","flat","flavor","flax","flesh","flight","flock","flood","floor","flower","flugelhorn","flute","fly","foam","fog","fold","font","food","foot","football","footnote","force","forecast","forehead","forest","forgery","fork","form","format","fortnight","foundation","fountain","fowl","fox","foxglove","fragrance","frame","france","freckle","freeze","freezer","freighter","french","freon","friction","Friday","fridge","friend","frog","front","frost","frown","fruit","fuel","fur","furniture","galley","gallon","game","gander","garage","garden","garlic","gas","gasoline","gate","gateway","gauge","gazelle","gear","gearshift","geese","gemini","gender","geography","geology","geometry","george","geranium","german","germany","ghana","ghost","giant","giraffe","girdle","girl","gladiolus","glass","glider","glockenspiel","glove","glue","goal","goat","gold","goldfish","golf","gondola","gong","good-bye","goose","gore-tex","gorilla","gosling","government","governor","grade","grain","gram","granddaughter","grandfather","grandmother","grandson","grape","graphic","grass","grasshopper","gray","grease","great-grandfather","great-grandmother","greece","greek","green","grenade","grey","grill","grip","ground","group","grouse","growth","guarantee","guatemalan","guide","guilty","guitar","gum","gun","gym","gymnast","hacksaw","hail","hair","haircut","half-brother","half-sister","halibut","hall","hallway","hamburger","hammer","hamster","hand","handball","handicap","handle","handsaw","harbor","hardboard","hardcover","hardhat","hardware","harmonica","harmony","harp","hat","hate","hawk","head","headlight","headline","health","hearing","heart","heat","heaven","hedge","height","helen","helicopter","helium","hell","helmet","help","hemp","hen","heron","herring","hexagon","hill","himalayan","hip","hippopotamus","history","hockey","hoe","hole","holiday","home","honey","hood","hook","hope","horn","horse","hose","hospital","hot","hour","hourglass","house","hovercraft","hub","hubcap","humidity","humor","hurricane","hyacinth","hydrant","hydrofoil","hydrogen","hyena","hygienic","ice","icebreaker","icicle","icon","idea","ikebana","illegal","imprisonment","improvement","impulse","inch","income","increase","index","india","indonesia","industry","ink","innocent","input","insect","instruction","instrument","insulation","insurance","interactive","interest","internet","interviewer","intestine","invention","inventory","invoice","iran","iraq","iris","iron","island","israel","italian","italy","jacket","jaguar","jail","jam","james","january","japan","japanese","jar","jasmine","jason","jaw","jeep","jelly","jellyfish","jennifer","jet","jewel","join","joke","joseph","journey","judge","judo","juice","july","jumbo","jump","jumper","june","jury","justice","jute","kale","kamikaze","kangaroo","karate","karen","kayak","kendo","kenneth","kenya","ketchup","kettle","kettledrum","kevin","key","keyboard","kick","kidney","kilogram","kilometer","kimberly","kiss","kitchen","kite","kitten","kitty","knee","knife","knight","knot","knowledge","kohlrabi","korean","laborer","lace","ladybug","lake","lamb","lamp","lan","land","landmine","language","larch","lasagna","latency","latex","lathe","laugh","laundry","laura","law","lawyer","layer","lead","leaf","leather","leek","leg","legal","lemonade","lentil","leo","leopard","letter","lettuce","level","libra","library","license","lier","lift","light","lightning","lilac","lily","limit","linda","line","linen","link","lion","lip","lipstick","liquid","liquor","lisa","list","literature","litter","liver","lizard","llama","loaf","loan","lobster","lock","locket","locust","look","loss","lotion","love","low","lumber","lunch","lunchroom","lung","lunge","lute","luttuce","lycra","lynx","lyocell","lyre","lyric","macaroni","machine","macrame","magazine","magic","magician","maid","mail","mailbox","mailman","makeup","malaysia","male","mall","mallet","man","manager","mandolin","manicure","manx","map","maple","maraca","marble","march","margaret","margin","maria","marimba","mark","market","married","mary","mascara","mask","mass","match","math","mattock","may","mayonnaise","meal","measure","meat","mechanic","medicine","meeting","melody","memory","men","menu","mercury","message","metal","meteorology","meter","methane","mexican","mexico","mice","michael","michelle","microwave","middle","mile","milk","milkshake","millennium","millimeter","millisecond","mimosa","mind","mine","minibus","mini-skirt","minister","mint","minute","mirror","missile","mist","mistake","mitten","moat","modem","mole","mom","Monday","money","monkey","month","moon","morning","morocco","mosque","mosquito","mother","mother-in-law","motion","motorboat","motorcycle","mountain","mouse","moustache","mouth","move","multi-hop","multimedia","muscle","museum","music","musician","mustard","myanmar","nail","name","nancy","napkin","narcissus","nation","neck","need","needle","neon","nepal","nephew","nerve","nest","net","network","newsprint","newsstand","nic","nickel","niece","nigeria","night","nitrogen","node","noise","noodle","norwegian","nose","note","notebook","notify","novel","november","number","numeric","nurse","nut","nylon","oak","oatmeal","objective","oboe","observation","occupation","ocean","ocelot","octagon","octave","october","octopus","odometer","offence","offer","office","oil","okra","olive","onion","open","opera","operation","ophthalmologist","opinion","option","orange","orchestra","orchid","order","organ","organisation","organization","ornament","ostrich","otter","ounce","output","outrigger","oval","oven","overcoat","owl","owner","ox","oxygen","oyster","package","packet","page","pail","pain","paint","pair","pajama","pakistan","palm","pamphlet","pan","pancake","pancreas","panda","pansy","panther","pantry","pair of pants","panty","pantyhose","paper","paperback","parade","parallelogram","parcel","parent","parenthesis","park","parrot","parsnip","part","particle","partner","partridge","party","passbook","passenger","passive","pasta","paste","pastor","pastry","patch","path","patient","patio","patricia","paul","payment","pea","peace","peak","peanut","pear","pedestrian","pediatrician","peen","peer-to-peer","pelican","pen","penalty","pencil","pendulum","pentagon","peony","pepper","perch","perfume","period","periodical","peripheral","permission","persian","person","peru","pest","pet","pharmacist","pheasant","philosophy","phone","physician","piano","piccolo","pickle","picture","pie","pig","pigeon","pike","pillow","pilot","pimple","pin","pine","ping","pink","pint","pipe","pisces","pizza","place","plain","plane","planet","plant","plantation","plaster","plasterboard","plastic","plate","platinum","play","playground","playroom","pleasure","plier","plot","plough","plow","plywood","pocket","poet","point","poison","poland","police","policeman","polish","politician","pollution","polo","polyester","pond","popcorn","poppy","population","porch","porcupine","port","porter","position","possibility","postage","postbox","pot","potato","poultry","pound","powder","power","precipitation","preface","prepared","pressure","price","priest","print","printer","prison","probation","process","produce","product","production","professor","profit","promotion","propane","property","prose","prosecution","protest","protocol","pruner","psychiatrist","psychology","ptarmigan","puffin","pull","puma","pump","pumpkin","punch","punishment","puppy","purchase","purple","purpose","push","pvc","pyjama","pyramid","quail","quality","quart","quarter","quartz","queen","question","quicksand","quiet","quill","quilt","quince","quit","quiver","quotation","rabbi","rabbit","radar","radiator","radio","radish","raft","rail","railway","rain","rainbow","raincoat","rainstorm","rake","ramie","random","range","rat","rate","raven","ravioli","ray","rayon","reaction","reading","reason","receipt","recess","record","recorder","rectangle","red","reduction","refrigerator","refund","regret","reindeer","relation","relative","religion","relish","reminder","repair","replace","report","representative","request","resolution","respect","responsibility","rest","restaurant","result","retailer","revolve","revolver","reward","rhinoceros","rhythm","rice","richard","riddle","rifle","ring","rise","risk","river","riverbed","road","roadway","roast","robert","robin","rock","rocket","rod","roll","romania","romanian","ronald","roof","room","rooster","root","rose","rotate","route","router","rowboat","rub","rubber","rugby","rule","run","russia","russian","rutabaga","ruth","sack","sagittarius","sail","sailboat","sailor","salad","salary","sale","salesman","salmon","salt","sampan","samurai","sand","sandra","sandwich","Santa","sardine","satin","Saturday","sauce","sausage","save","saw","saxophone","scale","scallion","scanner","scarecrow","scarf","scene","scent","schedule","school","science","scissor","scooter","scorpio","scorpion","scraper","screen","screw","screwdriver","sea","seagull","seal","seaplane","search","seashore","season","seat","second","secretary","secure","security","seed","seeder","segment","select","selection","self","semicircle","semicolon","sense","sentence","separated","september","servant","server","session","sex","shade","shadow","shake","shallot","shame","shampoo","shape","share","shark","sharon","shear","sheep","sheet","shelf","shell","shield","shingle","ship","shirt","shock","shoe","shoemaker","shop","pair of shorts","shoulder","shovel","show","shrimp","shrine","siamese","siberian","side","sideboard","sidecar","sidewalk","sign","signature","silica","silk","silver","sing","singer","single","sink","sister","sister-in-law","size","skate","ski","skill","skin","skirt","sky","slash","slave","sled","sleep","sleet","slice","slime","slip","slipper","slope","smash","smell","smile","smoke","snail","snake","sneeze","snow","snowboard","snowflake","snowman","snowplow","snowstorm","soap","soccer","society","sociology","sock","soda","sofa","softball","softdrink","software","soil","soldier","son","song","soprano","sort","sound","soup","sousaphone","soy","soybean","space","spade","spaghetti","spain","spandex","spark","sparrow","spear","specialist","speedboat","sphere","sphynx","spider","spike","spinach","spleen","sponge","spoon","spot","spring","sprout","spruce","spy","square","squash","squid","squirrel","stage","staircase","stamp","star","start","starter","state","statement","station","statistic","steam","steel","stem","step","step-aunt","step-brother","stepdaughter","step-daughter","step-father","step-grandfather","step-grandmother","stepmother","step-mother","step-sister","stepson","step-son","step-uncle","steven","stew","stick","stinger","stitch","stock","stocking","stomach","stone","stool","stop","stopsign","stopwatch","store","storm","story","stove","stranger","straw","stream","street","streetcar","stretch","string","structure","study","sturgeon","submarine","substance","subway","success","sudan","suede","sugar","suggestion","suit","summer","sun","Sunday","sundial","sunflower","sunshine","supermarket","supply","support","surfboard","surgeon","surname","surprise","susan","sushi","swallow","swamp","swan","sweater","sweatshirt","sweatshop","swedish","swim","swing","swiss","switch","sword","swordfish","sycamore","syrup","system","table","tablecloth","tabletop","tachometer","tadpole","tail","tailor","taiwan","talk","tank","tanker","tanzania","target","taste","taurus","tax","taxi","taxicab","tea","teacher","team","technician","teeth","television","teller","temper","temperature","temple","tempo","tendency","tennis","tenor","tent","territory","test","text","textbook","texture","thailand","theater","theory","thermometer","thing","thistle","thought","thread","thrill","throat","throne","thumb","thunder","thunderstorm","Thursday","ticket","tie","tiger","tile","timbale","time","timer","timpani","tin","tip","tire","titanium","title","toad","toast","toe","toenail","toilet","tomato","tom-tom","ton","tongue","tooth","toothbrush","toothpaste","top","tornado","tortellini","tortoise","touch","tower","town","toy","tractor","trade","traffic","trail","train","tramp","transaction","transmission","transport","trapezoid","tray","treatment","tree","trial","triangle","trick","trigonometry","trip","trombone","trouble","trouser","trout","trowel","truck","trumpet","trunk","t-shirt","tsunami","tub","tuba","Tuesday","tugboat","tulip","tuna","tune","turkey","turkish","turn","turnip","turnover","turret","turtle","tv","twig","twilight","twine","twist","typhoon","tyvek","uganda","umbrella","uncle","undercloth","underpant","undershirt","underwear","unit","unshielded","use","utensil","vacation","vacuum","valley","value","van","vase","vault","vegetable","vegetarian","veil","vein","velvet","verdict","vermicelli","verse","vessel","vest","veterinarian","vibraphone","Vietnam","view","vinyl","viola","violet","violin","virgo","viscose","vise","vision","visitor","voice","volcano","volleyball","voyage","vulture","waiter","waitress","walk","wall","wallaby","wallet","walrus","war","wash","washer","wasp","waste","watch","watchmaker","water","waterfall","wave","wax","way","wealth","weapon","weasel","weather","wedge","Wednesday","weed","weeder","week","weight","whale","wheel","whip","whiskey","whistle","white","wholesaler","whorl","wilderness","william","willow","wind","windchime","window","windscreen","windshield","wine","wing","winter","wire","wish","witch","withdrawal","witness","wolf","woman","women","wood","wool","woolen","word","work","workshop","worm","wound","wrecker","wren","wrench","wrinkle","wrist","writer","xylophone","yacht","yak","yam","yard","yarn","year","yellow","yew","yogurt","yoke","yugoslavian","zebra","zephyr","zinc","zipper","zone","zoo","zoology"]; diff --git a/web/index.php b/web/index.php index b591445..8782ac7 100644 --- a/web/index.php +++ b/web/index.php @@ -9,6 +9,40 @@ $url = array_filter(explode('/',ltrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL $backend = new OpenTrashmailBackend($url); +$settings = loadSettings(); + +if($settings['ALLOWED_IPS']) +{ + $ip = getUserIP(); + if(!isIPInRange( $ip, $settings['ALLOWED_IPS'] )) + exit("Your IP ($ip) is not allowed to access this site."); +} + +if($settings['PASSWORD']) //site requires a password +{ + session_start(); + $pw = $settings['PASSWORD']; + $auth = false; + //first check for auth header or POST/GET variable + if(isset($_SERVER['HTTP_PWD']) && $_SERVER['HTTP_PWD'] == $pw) + $auth = true; + else if(isset($_REQUEST['password']) && $_REQUEST['password'] == $pw) + $auth = true; + // if not, check for session + else if(isset($_SESSION['authenticated']) && $_SESSION['authenticated'] == true) + $auth = true; + // if user sent a pw but it's wrong, show error + else if($_REQUEST['password'] != $settings['PASSWORD']) + exit($backend->renderTemplate('password.html',[ + 'error'=>'Wrong password', + ])); + + if($auth===true) + $_SESSION['authenticated'] = true; + else + exit($backend->renderTemplate('password.html')); +} + if($_SERVER['HTTP_HX_REQUEST']!='true') { if(count($url)==0 || !file_exists(ROOT.DS.implode('/', $url))) diff --git a/web/templates/password.html.php b/web/templates/password.html.php new file mode 100644 index 0000000..6137832 --- /dev/null +++ b/web/templates/password.html.php @@ -0,0 +1,18 @@ + + + + + Password Form + + +

Enter Password

+
+ + +

+ +
+ +

+ +