|  | @@ -0,0 +1,127 @@
 | 
	
		
			
			|  | 1 | +#!/usr/bin/env python
 | 
	
		
			
			|  | 2 | +
 | 
	
		
			
			|  | 3 | +'''
 | 
	
		
			
			|  | 4 | +searx is free software: you can redistribute it and/or modify
 | 
	
		
			
			|  | 5 | +it under the terms of the GNU Affero General Public License as published by
 | 
	
		
			
			|  | 6 | +the Free Software Foundation, either version 3 of the License, or
 | 
	
		
			
			|  | 7 | +(at your option) any later version.
 | 
	
		
			
			|  | 8 | +
 | 
	
		
			
			|  | 9 | +searx is distributed in the hope that it will be useful,
 | 
	
		
			
			|  | 10 | +but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
	
		
			
			|  | 11 | +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
	
		
			
			|  | 12 | +GNU Affero General Public License for more details.
 | 
	
		
			
			|  | 13 | +
 | 
	
		
			
			|  | 14 | +You should have received a copy of the GNU Affero General Public License
 | 
	
		
			
			|  | 15 | +along with searx. If not, see < http://www.gnu.org/licenses/ >.
 | 
	
		
			
			|  | 16 | +
 | 
	
		
			
			|  | 17 | +(C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
 | 
	
		
			
			|  | 18 | +'''
 | 
	
		
			
			|  | 19 | +
 | 
	
		
			
			|  | 20 | +from searx.languages import language_codes
 | 
	
		
			
			|  | 21 | +from searx.engines import (
 | 
	
		
			
			|  | 22 | +    categories, engines, engine_shortcuts
 | 
	
		
			
			|  | 23 | +)
 | 
	
		
			
			|  | 24 | +import string
 | 
	
		
			
			|  | 25 | +import re
 | 
	
		
			
			|  | 26 | +
 | 
	
		
			
			|  | 27 | +
 | 
	
		
			
			|  | 28 | +class Query(object):
 | 
	
		
			
			|  | 29 | +    """parse query"""
 | 
	
		
			
			|  | 30 | +
 | 
	
		
			
			|  | 31 | +    def __init__(self, query, blocked_engines):
 | 
	
		
			
			|  | 32 | +        self.query = query
 | 
	
		
			
			|  | 33 | +        self.blocked_engines = []
 | 
	
		
			
			|  | 34 | +        
 | 
	
		
			
			|  | 35 | +        if blocked_engines:
 | 
	
		
			
			|  | 36 | +            self.blocked_engines = blocked_engines
 | 
	
		
			
			|  | 37 | +            
 | 
	
		
			
			|  | 38 | +        self.query_parts = []
 | 
	
		
			
			|  | 39 | +        self.engines = []
 | 
	
		
			
			|  | 40 | +        self.languages = []
 | 
	
		
			
			|  | 41 | +    
 | 
	
		
			
			|  | 42 | +    # parse query, if tags are set, which change the serch engine or search-language
 | 
	
		
			
			|  | 43 | +    def parse_query(self):
 | 
	
		
			
			|  | 44 | +        self.query_parts = []
 | 
	
		
			
			|  | 45 | +        
 | 
	
		
			
			|  | 46 | +        # split query, including whitespaces
 | 
	
		
			
			|  | 47 | +        raw_query_parts = re.split(r'(\s+)', self.query)
 | 
	
		
			
			|  | 48 | +        
 | 
	
		
			
			|  | 49 | +        parse_next = True
 | 
	
		
			
			|  | 50 | +        
 | 
	
		
			
			|  | 51 | +        for query_part in raw_query_parts:
 | 
	
		
			
			|  | 52 | +            if not parse_next:
 | 
	
		
			
			|  | 53 | +                self.query_parts[-1] += query_part
 | 
	
		
			
			|  | 54 | +                continue
 | 
	
		
			
			|  | 55 | +           
 | 
	
		
			
			|  | 56 | +            parse_next = False
 | 
	
		
			
			|  | 57 | +           
 | 
	
		
			
			|  | 58 | +            # part does only contain spaces, skip
 | 
	
		
			
			|  | 59 | +            if query_part.isspace()\
 | 
	
		
			
			|  | 60 | +               or query_part == '':
 | 
	
		
			
			|  | 61 | +                parse_next = True
 | 
	
		
			
			|  | 62 | +                self.query_parts.append(query_part)
 | 
	
		
			
			|  | 63 | +                continue
 | 
	
		
			
			|  | 64 | +
 | 
	
		
			
			|  | 65 | +            # this force a language            
 | 
	
		
			
			|  | 66 | +            if query_part[0] == ':':
 | 
	
		
			
			|  | 67 | +                lang = query_part[1:].lower()
 | 
	
		
			
			|  | 68 | +
 | 
	
		
			
			|  | 69 | +                # check if any language-code is equal with declared language-codes
 | 
	
		
			
			|  | 70 | +                for lc in language_codes:
 | 
	
		
			
			|  | 71 | +                    lang_id, lang_name, country = map(str.lower, lc)
 | 
	
		
			
			|  | 72 | +
 | 
	
		
			
			|  | 73 | +                    # if correct language-code is found, set it as new search-language
 | 
	
		
			
			|  | 74 | +                    if lang == lang_id\
 | 
	
		
			
			|  | 75 | +                       or lang_id.startswith(lang)\
 | 
	
		
			
			|  | 76 | +                       or lang == lang_name\
 | 
	
		
			
			|  | 77 | +                       or lang == country:
 | 
	
		
			
			|  | 78 | +                        parse_next = True
 | 
	
		
			
			|  | 79 | +                        self.languages.append(lang)
 | 
	
		
			
			|  | 80 | +                        break
 | 
	
		
			
			|  | 81 | +
 | 
	
		
			
			|  | 82 | +            # this force a engine or category
 | 
	
		
			
			|  | 83 | +            if query_part[0] == '!':
 | 
	
		
			
			|  | 84 | +                prefix = query_part[1:].replace('_', ' ')
 | 
	
		
			
			|  | 85 | +
 | 
	
		
			
			|  | 86 | +                # check if prefix is equal with engine shortcut
 | 
	
		
			
			|  | 87 | +                if prefix in engine_shortcuts\
 | 
	
		
			
			|  | 88 | +                   and not engine_shortcuts[prefix] in self.blocked_engines:
 | 
	
		
			
			|  | 89 | +                    parse_next = True
 | 
	
		
			
			|  | 90 | +                    self.engines.append({'category': 'none',
 | 
	
		
			
			|  | 91 | +                                         'name': engine_shortcuts[prefix]})
 | 
	
		
			
			|  | 92 | +                
 | 
	
		
			
			|  | 93 | +                # check if prefix is equal with engine name
 | 
	
		
			
			|  | 94 | +                elif prefix in engines\
 | 
	
		
			
			|  | 95 | +                        and not prefix in self.blocked_engines:
 | 
	
		
			
			|  | 96 | +                    parse_next = True
 | 
	
		
			
			|  | 97 | +                    self.engines.append({'category': 'none',
 | 
	
		
			
			|  | 98 | +                                        'name': prefix})
 | 
	
		
			
			|  | 99 | +
 | 
	
		
			
			|  | 100 | +                # check if prefix is equal with categorie name
 | 
	
		
			
			|  | 101 | +                elif prefix in categories:
 | 
	
		
			
			|  | 102 | +                    # using all engines for that search, which are declared under that categorie name
 | 
	
		
			
			|  | 103 | +                    parse_next = True
 | 
	
		
			
			|  | 104 | +                    self.engines.extend({'category': prefix,
 | 
	
		
			
			|  | 105 | +                                        'name': engine.name}
 | 
	
		
			
			|  | 106 | +                                        for engine in categories[prefix]
 | 
	
		
			
			|  | 107 | +                                        if not engine in self.blocked_engines)
 | 
	
		
			
			|  | 108 | +          
 | 
	
		
			
			|  | 109 | +            # append query part to query_part list
 | 
	
		
			
			|  | 110 | +            self.query_parts.append(query_part)
 | 
	
		
			
			|  | 111 | +
 | 
	
		
			
			|  | 112 | +    def changeSearchQuery(self, search_query):
 | 
	
		
			
			|  | 113 | +        if len(self.query_parts):
 | 
	
		
			
			|  | 114 | +            self.query_parts[-1] = search_query
 | 
	
		
			
			|  | 115 | +        else:
 | 
	
		
			
			|  | 116 | +            self.query_parts.append(search_query)
 | 
	
		
			
			|  | 117 | +            
 | 
	
		
			
			|  | 118 | +    def getSearchQuery(self):
 | 
	
		
			
			|  | 119 | +        if len(self.query_parts):
 | 
	
		
			
			|  | 120 | +            return self.query_parts[-1]
 | 
	
		
			
			|  | 121 | +        else:
 | 
	
		
			
			|  | 122 | +            return ''
 | 
	
		
			
			|  | 123 | +    
 | 
	
		
			
			|  | 124 | +    def getFullQuery(self):
 | 
	
		
			
			|  | 125 | +        # get full querry including whitespaces
 | 
	
		
			
			|  | 126 | +        return string.join(self.query_parts, '')
 | 
	
		
			
			|  | 127 | +
 |